aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/headless.js12
-rw-r--r--src/headlessCanvas.js1
-rw-r--r--src/library.js26
-rw-r--r--src/library_gl.js125
-rw-r--r--src/library_sdl.js24
-rw-r--r--src/modules.js15
-rw-r--r--src/preamble.js2
-rw-r--r--tests/cases/fptosi.ll28
-rw-r--r--tests/cases/fptosi.txt6
-rw-r--r--tests/glgettexenv.c71
-rw-r--r--tests/test_browser.py45
-rw-r--r--tests/test_core.py5
-rw-r--r--tests/test_float_literals.cpp92
-rw-r--r--tests/test_float_literals.out22
-rw-r--r--tests/test_other.py2
-rw-r--r--tools/file_packager.py8
-rw-r--r--tools/js-optimizer.js205
-rw-r--r--tools/test-js-optimizer-asm-regs-harder-output.js129
-rw-r--r--tools/test-js-optimizer-asm-regs-harder.js150
19 files changed, 839 insertions, 129 deletions
diff --git a/src/headless.js b/src/headless.js
index e5458641..5880c087 100644
--- a/src/headless.js
+++ b/src/headless.js
@@ -82,6 +82,16 @@ var window = {
}
listeners.push(func);
},
+ removeEventListener: function(id, func) {
+ var listeners = this.eventListeners[id];
+ if (!listeners) return;
+ for (var i = 0; i < listeners.length; i++) {
+ if (listeners[i] === func) {
+ listeners.splice(i, 1);
+ return;
+ }
+ }
+ },
callEventListeners: function(id) {
var listeners = this.eventListeners[id];
if (listeners) {
@@ -101,6 +111,7 @@ var document = {
headless: true,
eventListeners: {},
addEventListener: window.addEventListener,
+ removeEventListener: window.removeEventListener,
callEventListeners: window.callEventListeners,
getElementById: function(id) {
switch(id) {
@@ -144,6 +155,7 @@ var document = {
},
eventListeners: {},
addEventListener: document.addEventListener,
+ removeEventListener: document.removeEventListener,
callEventListeners: document.callEventListeners,
};
};
diff --git a/src/headlessCanvas.js b/src/headlessCanvas.js
index 4951aed8..6b0f9d47 100644
--- a/src/headlessCanvas.js
+++ b/src/headlessCanvas.js
@@ -600,6 +600,7 @@ function headlessCanvas() {
style: {},
eventListeners: {},
addEventListener: function(){},
+ removeEventListener: function(){},
requestFullScreen: function() {
document.fullscreenElement = document.getElementById('canvas');
window.setTimeout(function() {
diff --git a/src/library.js b/src/library.js
index f9af8a96..bc577e78 100644
--- a/src/library.js
+++ b/src/library.js
@@ -4291,6 +4291,8 @@ LibraryManager.library = {
abort('trap!');
},
+ llvm_prefetch: function(){},
+
__assert_fail: function(condition, filename, line, func) {
ABORT = true;
throw 'Assertion failed: ' + Pointer_stringify(condition) + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + stackTrace();
@@ -9168,6 +9170,30 @@ LibraryManager.library = {
// misc shims for musl
__lockfile: function() { return 1 },
__unlockfile: function(){},
+
+ // misc definitions to avoid unnecessary unresolved symbols from fastcomp
+ emscripten_prep_setjmp: true,
+ emscripten_check_longjmp: true,
+ emscripten_get_longjmp_result: true,
+ emscripten_setjmp: true,
+ emscripten_preinvoke: true,
+ emscripten_postinvoke: true,
+ emscripten_resume: true,
+ emscripten_landingpad: true,
+ getHigh32: true,
+ setHigh32: true,
+ FtoILow: true,
+ FtoIHigh: true,
+ DtoILow: true,
+ DtoIHigh: true,
+ BDtoILow: true,
+ BDtoIHigh: true,
+ SItoF: true,
+ UItoF: true,
+ SItoD: true,
+ UItoD: true,
+ BItoD: true,
+ llvm_dbg_value: true,
};
function autoAddDeps(object, name) {
diff --git a/src/library_gl.js b/src/library_gl.js
index edd5890e..0a30292a 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -957,7 +957,11 @@ var LibraryGL = {
usage = 0x88E8; // GL_DYNAMIC_DRAW
break;
}
- GLctx.bufferData(target, HEAPU8.subarray(data, data+size), usage);
+ if (!data) {
+ GLctx.bufferData(target, size, usage);
+ } else {
+ GLctx.bufferData(target, HEAPU8.subarray(data, data+size), usage);
+ }
},
glBufferSubData__sig: 'viiii',
@@ -2644,7 +2648,7 @@ var LibraryGL = {
return "float";
}
- return Abort_NoSupport("Unsupported combiner op: 0x" + op.toString(16));
+ return abort_noSupport("Unsupported combiner op: 0x" + op.toString(16));
}
function getCurTexUnit() {
@@ -2997,7 +3001,7 @@ var LibraryGL = {
}
}
- return Abort_NoSupport("Unsupported TexEnv mode: 0x" + this.mode.toString(16));
+ return abort_noSupport("Unsupported TexEnv mode: 0x" + this.mode.toString(16));
}
CTexEnv.prototype.genCombinerLines = function CTexEnv_getCombinerLines(isColor, outputVar,
@@ -3417,6 +3421,107 @@ var LibraryGL = {
Module.printErr('WARNING: Unhandled `pname` in call to `glTexEnvfv`.');
}
},
+
+ hook_getTexEnviv: function(target, pname, param) {
+ if (target != GL_TEXTURE_ENV)
+ return;
+
+ var env = getCurTexUnit().env;
+ switch (pname) {
+ case GL_TEXTURE_ENV_MODE:
+ {{{ makeSetValue('param', '0', 'env.mode', 'i32') }}};
+ return;
+
+ case GL_TEXTURE_ENV_COLOR:
+ {{{ makeSetValue('param', '0', 'Math.max(Math.min(env.envColor[0]*255, 255, -255))', 'i32') }}};
+ {{{ makeSetValue('param', '1', 'Math.max(Math.min(env.envColor[1]*255, 255, -255))', 'i32') }}};
+ {{{ makeSetValue('param', '2', 'Math.max(Math.min(env.envColor[2]*255, 255, -255))', 'i32') }}};
+ {{{ makeSetValue('param', '3', 'Math.max(Math.min(env.envColor[3]*255, 255, -255))', 'i32') }}};
+ return;
+
+ case GL_COMBINE_RGB:
+ {{{ makeSetValue('param', '0', 'env.colorCombiner', 'i32') }}};
+ return;
+
+ case GL_COMBINE_ALPHA:
+ {{{ makeSetValue('param', '0', 'env.alphaCombiner', 'i32') }}};
+ return;
+
+ case GL_SRC0_RGB:
+ {{{ makeSetValue('param', '0', 'env.colorSrc[0]', 'i32') }}};
+ return;
+
+ case GL_SRC1_RGB:
+ {{{ makeSetValue('param', '0', 'env.colorSrc[1]', 'i32') }}};
+ return;
+
+ case GL_SRC2_RGB:
+ {{{ makeSetValue('param', '0', 'env.colorSrc[2]', 'i32') }}};
+ return;
+
+ case GL_SRC0_ALPHA:
+ {{{ makeSetValue('param', '0', 'env.alphaSrc[0]', 'i32') }}};
+ return;
+
+ case GL_SRC1_ALPHA:
+ {{{ makeSetValue('param', '0', 'env.alphaSrc[1]', 'i32') }}};
+ return;
+
+ case GL_SRC2_ALPHA:
+ {{{ makeSetValue('param', '0', 'env.alphaSrc[2]', 'i32') }}};
+ return;
+
+ case GL_OPERAND0_RGB:
+ {{{ makeSetValue('param', '0', 'env.colorOp[0]', 'i32') }}};
+ return;
+
+ case GL_OPERAND1_RGB:
+ {{{ makeSetValue('param', '0', 'env.colorOp[1]', 'i32') }}};
+ return;
+
+ case GL_OPERAND2_RGB:
+ {{{ makeSetValue('param', '0', 'env.colorOp[2]', 'i32') }}};
+ return;
+
+ case GL_OPERAND0_ALPHA:
+ {{{ makeSetValue('param', '0', 'env.alphaOp[0]', 'i32') }}};
+ return;
+
+ case GL_OPERAND1_ALPHA:
+ {{{ makeSetValue('param', '0', 'env.alphaOp[1]', 'i32') }}};
+ return;
+
+ case GL_OPERAND2_ALPHA:
+ {{{ makeSetValue('param', '0', 'env.alphaOp[2]', 'i32') }}};
+ return;
+
+ case GL_RGB_SCALE:
+ {{{ makeSetValue('param', '0', 'env.colorScale', 'i32') }}};
+ return;
+
+ case GL_ALPHA_SCALE:
+ {{{ makeSetValue('param', '0', 'env.alphaScale', 'i32') }}};
+ return;
+
+ default:
+ Module.printErr('WARNING: Unhandled `pname` in call to `glGetTexEnvi`.');
+ }
+ },
+
+ hook_getTexEnvfv: function(target, pname, param) {
+ if (target != GL_TEXTURE_ENV)
+ return;
+
+ var env = getCurTexUnit().env;
+ switch (pname) {
+ case GL_TEXTURE_ENV_COLOR:
+ {{{ makeSetValue('param', '0', 'env.envColor[0]', 'float') }}};
+ {{{ makeSetValue('param', '4', 'env.envColor[1]', 'float') }}};
+ {{{ makeSetValue('param', '8', 'env.envColor[2]', 'float') }}};
+ {{{ makeSetValue('param', '12', 'env.envColor[3]', 'float') }}};
+ return;
+ }
+ }
};
},
@@ -3738,7 +3843,8 @@ var LibraryGL = {
GLctx.bindAttribLocation(this.program, GLImmediate.VERTEX, 'a_position');
GLctx.bindAttribLocation(this.program, GLImmediate.COLOR, 'a_color');
GLctx.bindAttribLocation(this.program, GLImmediate.NORMAL, 'a_normal');
- for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
+ var maxVertexAttribs = GLctx.getParameter(GLctx.MAX_VERTEX_ATTRIBS);
+ for (var i = 0; i < GLImmediate.MAX_TEXTURES && GLImmediate.TEXTURE0 + i < maxVertexAttribs; i++) {
GLctx.bindAttribLocation(this.program, GLImmediate.TEXTURE0 + i, 'a_texCoord'+i);
GLctx.bindAttribLocation(this.program, GLImmediate.TEXTURE0 + i, aTexCoordPrefix+i);
}
@@ -4093,6 +4199,14 @@ var LibraryGL = {
//glTexEnvfv(target, pname, param);
};
+ _glGetTexEnviv = function _glGetTexEnviv(target, pname, param) {
+ GLImmediate.TexEnvJIT.hook_getTexEnviv(target, pname, param);
+ };
+
+ _glGetTexEnvfv = function _glGetTexEnvfv(target, pname, param) {
+ GLImmediate.TexEnvJIT.hook_getTexEnvfv(target, pname, param);
+ };
+
var glGetIntegerv = _glGetIntegerv;
_glGetIntegerv = function _glGetIntegerv(pname, params) {
switch (pname) {
@@ -4885,6 +4999,9 @@ var LibraryGL = {
glTexEnvf: function() { Runtime.warnOnce('glTexEnvf: TODO') },
glTexEnvfv: function() { Runtime.warnOnce('glTexEnvfv: TODO') },
+ glGetTexEnviv: function(target, pname, param) { throw 'GL emulation not initialized!'; },
+ glGetTexEnvfv: function(target, pname, param) { throw 'GL emulation not initialized!'; },
+
glTexImage1D: function() { throw 'glTexImage1D: TODO' },
glTexCoord3f: function() { throw 'glTexCoord3f: TODO' },
glGetTexLevelParameteriv: function() { throw 'glGetTexLevelParameteriv: TODO' },
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 5a111289..80734d95 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -223,19 +223,19 @@ var LibrarySDL = {
var is_SDL_HWPALETTE = flags & 0x00200000;
var bpp = is_SDL_HWPALETTE ? 1 : 4;
- {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.flags, 'flags', 'i32') }}} // SDL_Surface.flags
- {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.format, 'pixelFormat', 'void*') }}} // SDL_Surface.format TODO
- {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.w, 'width', 'i32') }}} // SDL_Surface.w
- {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.h, 'height', 'i32') }}} // SDL_Surface.h
- {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pitch, 'width * bpp', 'i32') }}} // SDL_Surface.pitch, assuming RGBA or indexed for now,
+ {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.flags, 'flags', 'i32') }}}; // SDL_Surface.flags
+ {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.format, 'pixelFormat', 'void*') }}};// SDL_Surface.format TODO
+ {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.w, 'width', 'i32') }}}; // SDL_Surface.w
+ {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.h, 'height', 'i32') }}}; // SDL_Surface.h
+ {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pitch, 'width * bpp', 'i32') }}}; // SDL_Surface.pitch, assuming RGBA or indexed for now,
// since that is what ImageData gives us in browsers
- {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'buffer', 'void*') }}} // SDL_Surface.pixels
- {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect, '0', 'i32*') }}} // SDL_Surface.offset
+ {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'buffer', 'void*') }}}; // SDL_Surface.pixels
+ {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect, '0', 'i32*') }}}; // SDL_Surface.offset
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.refcount, '1', 'i32') }}};
- {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.format, cDefine('SDL_PIXELFORMAT_RGBA8888'), 'i32') }}} // SDL_PIXELFORMAT_RGBA8888
- {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.palette, '0', 'i32') }}} // TODO
+ {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.format, cDefine('SDL_PIXELFORMAT_RGBA8888'), 'i32') }}};// SDL_PIXELFORMAT_RGBA8888
+ {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.palette, '0', 'i32') }}};// TODO
{{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BitsPerPixel, 'bpp * 8', 'i8') }}};
{{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BytesPerPixel, 'bpp', 'i8') }}};
@@ -594,7 +594,7 @@ var LibrarySDL = {
{{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
{{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.state, 'down ? 1 : 0', 'i8') }}};
- {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.repeat, '0', 'i8') }}} // TODO
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.repeat, '0', 'i8') }}}; // TODO
{{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.scancode, 'scan', 'i32') }}};
{{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.sym, 'key', 'i32') }}};
{{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.mod, 'SDL.modState', 'i16') }}};
@@ -879,8 +879,8 @@ var LibrarySDL = {
SDL_GetVideoInfo: function() {
// %struct.SDL_VideoInfo = type { i32, i32, %struct.SDL_PixelFormat*, i32, i32 } - 5 fields of quantum size
var ret = _malloc(5*Runtime.QUANTUM_SIZE);
- {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*0', '0', '0', 'i32') }}} // TODO
- {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*1', '0', '0', 'i32') }}} // TODO
+ {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*0', '0', '0', 'i32') }}}; // TODO
+ {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*1', '0', '0', 'i32') }}}; // TODO
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*2', '0', '0', 'void*') }}};
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*3', '0', 'Module["canvas"].width', 'i32') }}};
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*4', '0', 'Module["canvas"].height', 'i32') }}};
diff --git a/src/modules.js b/src/modules.js
index ea1509e9..2d2a75d0 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -426,7 +426,20 @@ var LibraryManager = {
var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_idbfs.js', 'library_memfs.js', 'library_nodefs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js', 'library_uuid.js', 'library_glew.js', 'library_html5.js'].concat(additionalLibraries);
for (var i = 0; i < libraries.length; i++) {
- eval(processMacros(preprocess(read(libraries[i]))));
+ var filename = libraries[i];
+ var src = read(filename);
+ try {
+ var processed = processMacros(preprocess(src));
+ eval(processed);
+ } catch(e) {
+ var details = [e, e.lineNumber ? 'line number: ' + e.lineNumber : '', (e.stack || "").toString().replace('Object.<anonymous>', filename)];
+ if (processed) {
+ error('failure to execute js library "' + filename + '": ' + details + '\npreprocessed source (you can run a js engine on this to get a clearer error message sometimes):\n=============\n' + processed + '\n=============\n');
+ } else {
+ error('failure to process js library "' + filename + '": ' + details + '\noriginal source:\n=============\n' + src + '\n=============\n');
+ }
+ throw e;
+ }
}
/*
diff --git a/src/preamble.js b/src/preamble.js
index d415b87e..9a65b092 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -6,6 +6,8 @@
{{RUNTIME}}
+Module['Runtime'] = Runtime;
+
#if ASM_JS
#if RESERVED_FUNCTION_POINTERS
function jsCall() {
diff --git a/tests/cases/fptosi.ll b/tests/cases/fptosi.ll
new file mode 100644
index 00000000..71bc6af8
--- /dev/null
+++ b/tests/cases/fptosi.ll
@@ -0,0 +1,28 @@
+; ModuleID = '/dev/shm/tmp/src.cpp.o'
+target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32"
+target triple = "le32-unknown-nacl"
+
+@.str = private unnamed_addr constant [8 x i8] c"*%.3f*\0A\00", align 1 ; [#uses=1 type=[8 x i8]*]
+@.str2 = private unnamed_addr constant [6 x i8] c"*%d*\0A\00", align 1 ; [#uses=1 type=[6 x i8]*]
+
+; [#uses=0]
+define i32 @main() {
+entry:
+ %f = fadd float 1.000, 0.500
+ %d = fadd double 3.333, 0.444
+ %fd = fpext float %f to double
+ %call1 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([8 x i8]* @.str, i32 0, i32 0), double %fd) ; [#uses=0 type=i32]
+ %call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([8 x i8]* @.str, i32 0, i32 0), double %d) ; [#uses=0 type=i32]
+ %fs = fptosi float %f to i64
+ %fu = fptoui float %f to i64
+ %ds = fptosi double %d to i64
+ %du = fptoui double %d to i64
+ %call3 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str2, i32 0, i32 0), i64 %fs) ; [#uses=0 type=i32]
+ %call4 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str2, i32 0, i32 0), i64 %fu) ; [#uses=0 type=i32]
+ %call5 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str2, i32 0, i32 0), i64 %ds) ; [#uses=0 type=i32]
+ %call6 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str2, i32 0, i32 0), i64 %du) ; [#uses=0 type=i32]
+ ret i32 1
+}
+
+; [#uses=1]
+declare i32 @printf(i8*, ...)
diff --git a/tests/cases/fptosi.txt b/tests/cases/fptosi.txt
new file mode 100644
index 00000000..eea925c4
--- /dev/null
+++ b/tests/cases/fptosi.txt
@@ -0,0 +1,6 @@
+*1.500*
+*3.777*
+*1*
+*1*
+*3*
+*3*
diff --git a/tests/glgettexenv.c b/tests/glgettexenv.c
new file mode 100644
index 00000000..a051a690
--- /dev/null
+++ b/tests/glgettexenv.c
@@ -0,0 +1,71 @@
+/*
+THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION
+AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN.
+
+THE ORIGINAL AUTHOR IS KYLE FOLEY.
+
+THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY
+OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF
+MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE,
+ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE
+RESULTING FROM THE USE, MODIFICATION, OR
+REDISTRIBUTION OF THIS SOFTWARE.
+*/
+
+#if !EMSCRIPTEN
+#define USE_GLEW 1
+#endif
+
+#if USE_GLEW
+#include "GL/glew.h"
+#endif
+
+#include "SDL/SDL.h"
+#if !USE_GLEW
+#include "SDL/SDL_opengl.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+int main(int argc, char *argv[])
+{
+ SDL_Surface *screen;
+ if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) {
+ printf("Unable to initialize SDL: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
+ screen = SDL_SetVideoMode( 640, 480, 24, SDL_OPENGL );
+ if ( !screen ) {
+ printf("Unable to set video mode: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ GLint value = 0;
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
+ glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &value);
+ assert(value == GL_BLEND);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &value);
+ assert(value == GL_MODULATE);
+
+ GLfloat colora[4] = { 0.2f, 0.3f, 0.4f, 0.5f };
+ GLfloat colorb[4] = {};
+ glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, colora);
+ glGetTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, colorb);
+ printf("%f %f %f %f\n", colorb[0], colorb[1], colorb[2], colorb[3]);
+ assert(colora[0] == colorb[0]);
+ assert(colora[1] == colorb[1]);
+ assert(colora[2] == colorb[2]);
+ assert(colora[3] == colorb[3]);
+ SDL_Quit();
+
+#ifdef REPORT_RESULT
+ int result = 1;
+ REPORT_RESULT();
+#endif
+ return 0;
+}
diff --git a/tests/test_browser.py b/tests/test_browser.py
index 226eddee..f185c211 100644
--- a/tests/test_browser.py
+++ b/tests/test_browser.py
@@ -580,6 +580,45 @@ If manually bisecting:
shutil.rmtree(os.path.join(self.get_dir(), 'subdirr'))
self.run_browser('page.html', 'You should see two cool numbers', '/report_result?1')
+ def test_custom_file_package_url(self):
+ # a few files inside a directory
+ self.clear()
+ os.makedirs(os.path.join(self.get_dir(), 'subdirr'));
+ os.makedirs(os.path.join(self.get_dir(), 'cdn'));
+ open(os.path.join(self.get_dir(), 'subdirr', 'data1.txt'), 'w').write('''1214141516171819''')
+ # change the file package base dir to look in a "cdn". note that normally you would add this in your own custom html file etc., and not by
+ # modifying the existing shell in this manner
+ open(self.in_dir('shell.html'), 'w').write(open(path_from_root('src', 'shell.html')).read().replace('var Module = {', 'var Module = { filePackagePrefixURL: "cdn/", '))
+ open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r'''
+ #include <stdio.h>
+ #include <string.h>
+ #include <emscripten.h>
+ int main() {
+ char buf[17];
+
+ FILE *f = fopen("subdirr/data1.txt", "r");
+ fread(buf, 1, 16, f);
+ buf[16] = 0;
+ fclose(f);
+ printf("|%s|\n", buf);
+ int result = !strcmp("1214141516171819", buf);
+
+ REPORT_RESULT();
+ return 0;
+ }
+ '''))
+
+ def test():
+ Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--shell-file', 'shell.html', '--preload-file', 'subdirr/data1.txt', '-o', 'test.html']).communicate()
+ shutil.move('test.data', os.path.join('cdn', 'test.data'))
+ self.run_browser('test.html', '', '/report_result?1')
+
+ test()
+
+ # TODO: CORS, test using a full url for filePackagePrefixURL
+ #open(self.in_dir('shell.html'), 'w').write(open(path_from_root('src', 'shell.html')).read().replace('var Module = {', 'var Module = { filePackagePrefixURL: "http:/localhost:8888/cdn/", '))
+ #test()
+
def test_compressed_file(self):
open(os.path.join(self.get_dir(), 'datafile.txt'), 'w').write('compress this please' + (2000*'.'))
open(os.path.join(self.get_dir(), 'datafile2.txt'), 'w').write('moar' + (100*'!'))
@@ -683,6 +722,9 @@ If manually bisecting:
def test_sdl_canvas(self):
self.btest('sdl_canvas.c', expected='1', args=['-s', 'LEGACY_GL_EMULATION=1'])
+ # some extra coverage
+ self.btest('sdl_canvas.c', expected='1', args=['-s', 'LEGACY_GL_EMULATION=1', '-s', '-O0', 'SAFE_HEAP=1'])
+ self.btest('sdl_canvas.c', expected='1', args=['-s', 'LEGACY_GL_EMULATION=1', '-s', '-O2', 'SAFE_HEAP=1'])
def test_sdl_canvas_proxy(self):
def post():
@@ -1543,6 +1585,9 @@ keydown(100);keyup(100); // trigger the end
def test_cube_explosion(self):
self.btest('cube_explosion.c', reference='cube_explosion.png', args=['-s', 'LEGACY_GL_EMULATION=1'])
+ def test_glgettexenv(self):
+ self.btest('glgettexenv.c', args=['-s', 'LEGACY_GL_EMULATION=1'], expected=['1'])
+
def test_sdl_canvas_blank(self):
self.btest('sdl_canvas_blank.c', reference='sdl_canvas_blank.png')
diff --git a/tests/test_core.py b/tests/test_core.py
index 311f33a0..99c69459 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -5954,7 +5954,7 @@ def process(filename):
def test_debug(self):
if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g')
if self.emcc_args is not None:
- if '-O1' in self.emcc_args or '-O2' in self.emcc_args: return self.skip('optimizations remove LLVM debug info')
+ if '-O1' in self.emcc_args or '-O2' in self.emcc_args or '-O3' in self.emcc_args: return self.skip('optimizations remove LLVM debug info')
src = '''
#include <stdio.h>
@@ -6267,6 +6267,9 @@ def process(filename):
self.do_run(src.replace('TYPE', 'unsigned int'), '*2147483645**2**-5**5*')
Settings.CORRECT_SIGNS = 0
+ def test_float_literals(self):
+ self.do_run_from_file(path_from_root('tests', 'test_float_literals.cpp'), path_from_root('tests', 'test_float_literals.out'))
+
def test_exit_status(self):
if self.emcc_args is None: return self.skip('need emcc')
src = r'''
diff --git a/tests/test_float_literals.cpp b/tests/test_float_literals.cpp
new file mode 100644
index 00000000..fdae2764
--- /dev/null
+++ b/tests/test_float_literals.cpp
@@ -0,0 +1,92 @@
+#include <limits>
+#include <math.h>
+#include <float.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_MSC_VER) || defined(EMSCRIPTEN)
+#define FLOAT_NAN ((float)std::numeric_limits<float>::quiet_NaN())
+#define FLOAT_INF ((float)std::numeric_limits<float>::infinity())
+#else
+#define FLOAT_NAN ((float)NAN)
+#define FLOAT_INF ((float)INFINITY)
+#endif
+
+#if defined(_MSC_VER) || defined(EMSCRIPTEN)
+#define DOUBLE_NAN ((double)std::numeric_limits<double>::quiet_NaN())
+#define DOUBLE_INF ((double)std::numeric_limits<double>::infinity())
+#else
+#define DOUBLE_NAN ((double)NAN)
+#define DOUBLE_INF ((double)INFINITY)
+#endif
+
+#ifdef _MSC_VER
+#define NOINLINE
+#else
+#define NOINLINE __attribute__((noinline))
+#endif
+
+float NOINLINE ret_e() { return (float)2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274; }
+float NOINLINE ret_minuspi() { return (float)-3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679; }
+float NOINLINE val() { return 10.f; }
+float NOINLINE val2() { return -10.f; }
+float NOINLINE zero() { return 0.f; }
+float NOINLINE zero2() { return -0.f; }
+
+double NOINLINE dret_e() { return (double)2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274; }
+double NOINLINE dret_minuspi() { return (double)-3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679; }
+double NOINLINE dval() { return 10.0; }
+double NOINLINE dval2() { return -10.0; }
+double NOINLINE dzero() { return 0.0; }
+double NOINLINE dzero2() { return -0.0; }
+
+const float e = ret_e();
+const float negpi = ret_minuspi();
+const float inf = FLOAT_INF;
+const float negInf = -FLOAT_INF;
+const float floatNan = FLOAT_NAN;
+const float floatMax = FLT_MAX;
+const float floatMin = -FLT_MAX;
+const float posValue = val();
+const float negValue = val2();
+const float posZero = zero();
+const float negZero = zero2();
+
+const double de = dret_e();
+const double dnegpi = dret_minuspi();
+const double dinf = DOUBLE_INF;
+const double dnegInf = -DOUBLE_INF;
+const double doubleNan = DOUBLE_NAN;
+const double doubleMax = DBL_MAX;
+const double doubleMin = -DBL_MAX;
+const double dposValue = dval();
+const double dnegValue = dval2();
+const double dposZero = dzero();
+const double dnegZero = dzero2();
+
+int main()
+{
+ printf("e: %f\n", e);
+ printf("negpi: %f\n", negpi);
+ printf("inf: %f\n", inf);
+ printf("negInf: %f\n", negInf);
+ printf("floatNan: %f\n", floatNan);
+ printf("floatMax: %f\n", floatMax);
+ printf("floatMin: %f\n", floatMin);
+ printf("posValue: %f\n", posValue);
+ printf("negValue: %f\n", negValue);
+ printf("posZero: %f\n", posZero);
+ printf("negZero: %f\n", negZero);
+
+ printf("e: %f\n", de);
+ printf("negpi: %f\n", dnegpi);
+ printf("inf: %f\n", dinf);
+ printf("negInf: %f\n", dnegInf);
+ printf("doubleNan: %f\n", doubleNan);
+ printf("doubleMax: %f\n", doubleMax);
+ printf("doubleMin: %f\n", doubleMin);
+ printf("posValue: %f\n", dposValue);
+ printf("negValue: %f\n", dnegValue);
+ printf("posZero: %f\n", dposZero);
+ printf("negZero: %f\n", dnegZero);
+}
diff --git a/tests/test_float_literals.out b/tests/test_float_literals.out
new file mode 100644
index 00000000..ab52d6c4
--- /dev/null
+++ b/tests/test_float_literals.out
@@ -0,0 +1,22 @@
+e: 2.718282
+negpi: -3.141593
+inf: inf
+negInf: -inf
+floatNan: nan
+floatMax: 3.4028234663852886e+38
+floatMin: -3.4028234663852886e+38
+posValue: 10.000000
+negValue: -10.000000
+posZero: 0.000000
+negZero: -0.000000
+e: 2.718282
+negpi: -3.141593
+inf: inf
+negInf: -inf
+doubleNan: nan
+doubleMax: 1.7976931348623157e+308
+doubleMin: -1.7976931348623157e+308
+posValue: 10.000000
+negValue: -10.000000
+posZero: 0.000000
+negZero: -0.000000
diff --git a/tests/test_other.py b/tests/test_other.py
index 53128391..8895a911 100644
--- a/tests/test_other.py
+++ b/tests/test_other.py
@@ -1773,6 +1773,8 @@ f.close()
['asm', 'eliminate']),
(path_from_root('tools', 'test-js-optimizer-asm-regs.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-output.js')).read(),
['asm', 'registerize']),
+ (path_from_root('tools', 'test-js-optimizer-asm-regs-harder.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-harder-output.js')).read(),
+ ['asm', 'registerizeHarder']),
(path_from_root('tools', 'test-js-optimizer-asm-regs-min.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-min-output.js')).read(),
['asm', 'registerize', 'minifyLocals']),
(path_from_root('tools', 'test-js-optimizer-asm-pre.js'), open(path_from_root('tools', 'test-js-optimizer-asm-pre-output.js')).read(),
diff --git a/tools/file_packager.py b/tools/file_packager.py
index 8b65b219..9699730f 100644
--- a/tools/file_packager.py
+++ b/tools/file_packager.py
@@ -462,7 +462,7 @@ if has_preloaded:
package_uuid = uuid.uuid4();
remote_package_name = os.path.basename(Compression.compressed_name(data_target) if Compression.on else data_target)
- code += r'''
+ ret += r'''
var PACKAGE_PATH;
if (typeof window === 'object') {
PACKAGE_PATH = window['encodeURIComponent'](window.location.pathname.toString().substring(0, window.location.pathname.toString().lastIndexOf('/')) + '/');
@@ -471,7 +471,7 @@ if has_preloaded:
PACKAGE_PATH = encodeURIComponent(location.pathname.toString().substring(0, location.pathname.toString().lastIndexOf('/')) + '/');
}
var PACKAGE_NAME = '%s';
- var REMOTE_PACKAGE_NAME = '%s';
+ var REMOTE_PACKAGE_NAME = (Module['filePackagePrefixURL'] || '') + '%s';
var PACKAGE_UUID = '%s';
''' % (data_target, remote_package_name, package_uuid)
@@ -666,7 +666,7 @@ if has_preloaded:
# Only tricky bit is the fetch is async, but also when runWithFS is called is async, so we handle both orderings.
ret += r'''
var fetched = null, fetchedCallback = null;
- fetchRemotePackage('%s', function(data) {
+ fetchRemotePackage(REMOTE_PACKAGE_NAME, function(data) {
if (fetchedCallback) {
fetchedCallback(data);
fetchedCallback = null;
@@ -674,7 +674,7 @@ if has_preloaded:
fetched = data;
}
}, handleError);
- ''' % os.path.basename(Compression.compressed_name(data_target) if Compression.on else data_target)
+ '''
code += r'''
Module.preloadResults[PACKAGE_NAME] = {fromCache: false};
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index b35da99d..21d521fd 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -2460,55 +2460,40 @@ function registerizeHarder(ast) {
buildFlowGraph(node[2]);
break;
case 'switch':
- // This is horrific. It need to capture the default flow-through
- // logic of sequential case bodies, as well as the theoretical
- // sequential evaluation of each case clause.
- // TODO: simplify based on asmjs switch-statement restrictions.
+ // Emscripten generates switch statements of a very limited
+ // form: all case clauses are numeric literals, and all
+ // case bodies end with a break. So it's basically equivalent
+ // to a multi-way 'if' statement.
isInExpr++;
buildFlowGraph(node[1]);
isInExpr--;
var jCheckExit = markJunction();
var jExit = addJunction();
pushActiveLabels(null, jExit);
- // Process all cases as a sequential chain of checks.
- // Process all case bodies as one big flow-through statement.
- // They might break themselves out of it but this implements the
- // default fall-through case logic.
var hasDefault = false;
- var jPrevCaseExit = jCheckExit;
- var jPrevBodyExit = jCheckExit;
for (var i=0; i<node[2].length; i++) {
- // In the general case we'll need a basic block for the case clause.
- // Try to avoid it for common, simple, non-var-using cases.
+ setJunction(jCheckExit);
if (!node[2][i][0]) {
hasDefault = true;
} else {
if (node[2][i][0][0] !== 'num') {
- setJunction(jPrevCaseExit);
- isInExpr++;
- buildFlowGraph(node[2][i][0]);
- isInExpr--;
- jPrevCaseExit = markJunction();
+ if (node[2][i][0][0] !== 'unary-prefix' || node[2][i][0][2][0] !== 'num') {
+ assert(false, 'non-numeric switch case clause');
+ }
}
- }
- // The next case body flows from the exit of the prev one,
- // or may be entered directly from the case statement exit.
- setJunction(jPrevCaseExit);
- if (jPrevBodyExit !== jCheckExit) {
- joinJunction(jPrevBodyExit);
+ addPreCondTrue(['binary', '==', node[1], node[2][i][0]]);
}
for (var j = 0; j < node[2][i][1].length; j++) {
buildFlowGraph(node[2][i][1][j]);
}
- jPrevBodyExit = markJunction();
+ if (currEntryJunction !== null, 'switch case body did not break');
}
- markJunction(jExit);
// If there was no default case, we also need an empty block
- // linking straight from entry to exit.
- if (!hasDefault && jCheckExit !== jPrevBodyExit) {
+ // linking straight from the test evaluation to the exit.
+ if (!hasDefault) {
setJunction(jCheckExit);
- joinJunction(jExit);
}
+ markJunction(jExit);
popActiveLabels()
break;
case 'return':
@@ -2612,6 +2597,7 @@ function registerizeHarder(ast) {
if (labelCond && labelCond[0] === '==') {
// If there are multiple blocks with the same label, all bets are off.
// This seems to happen sometimes for short blocks that end with a return.
+ // TODO: it should be safe to merge the duplicates if they're identical.
if (labelCond[1] in labelledBlocks) {
labelledBlocks = {};
labelledJumps = [];
@@ -2711,6 +2697,7 @@ function registerizeHarder(ast) {
var link = {};
var lastUseLoc = {};
var firstDeadLoc = {};
+ var firstKillLoc = {};
var lastKillLoc = {};
for (var name in live) {
link[name] = name;
@@ -2735,6 +2722,7 @@ function registerizeHarder(ast) {
delete use[name];
delete live[name];
firstDeadLoc[name] = j;
+ firstKillLoc[name] = j;
if (lastUseLoc[name] === undefined) {
lastUseLoc[name] = j;
}
@@ -2760,6 +2748,9 @@ function registerizeHarder(ast) {
for (var name in lastUseLoc) {
lastUseLoc[name] -= n;
}
+ for (var name in firstKillLoc) {
+ firstKillLoc[name] -= n;
+ }
for (var name in lastKillLoc) {
lastKillLoc[name] -= n;
}
@@ -2791,6 +2782,7 @@ function registerizeHarder(ast) {
block.link = link;
block.lastUseLoc = lastUseLoc;
block.firstDeadLoc = firstDeadLoc;
+ block.firstKillLoc = firstKillLoc;
block.lastKillLoc = lastKillLoc;
}
@@ -2975,47 +2967,41 @@ function registerizeHarder(ast) {
if (block.nodes.length === 0) continue;
var jEnter = junctions[block.entry];
var jExit = junctions[block.exit];
- // Calculate the full internal liveness states for this block.
- var liveness = [{}];
- for (var name in jExit.live) {
- liveness[0][name] = 1;
- }
- for (var j=block.nodes.length-1; j>=0; j--) {
- var node = block.nodes[j];
- liveness.unshift(copy(liveness[0]));
- if (node[0] === 'assign') {
- var name = node[2][1];
- delete liveness[0][name];
- } else if (node[0] === 'name') {
- var name = node[1];
- liveness[0][name] = 1;
- } else {
- assert(false, 'unexpected node type: ' + node[0]);
- }
- }
- assert(liveness.length == block.nodes.length + 1);
- assert(setSize(setSub(liveness[0], jEnter.live)) == 0);
- // Mark the point at which each output reg gets assigned.
- // Variables that live past this point must not be assigned
+ // Mark the point at which each input reg becomes dead.
+ // Variables alive before this point must not be assigned
// to that register.
- var outputAssignLoc = {};
- var outputVars = {};
+ var inputVars = {}
+ var inputDeadLoc = {};
+ var inputVarsByReg = {};
for (var name in jExit.live) {
- var reg = junctionVariables[name].reg;
- assert(reg !== null, 'output variable doesnt have a register');
- outputAssignLoc[reg] = block.lastKillLoc[name];
- outputVars[reg] = name;
- }
- // Scan through in execution order, allocating registers on demand.
- // Be careful to avoid conflicts with the output registers.
+ if (!(name in block.kill)) {
+ inputVars[name] = 1;
+ var reg = junctionVariables[name].reg;
+ assert(reg !== null, 'input variable doesnt have a register');
+ inputDeadLoc[reg] = block.firstDeadLoc[name];
+ inputVarsByReg[reg] = name;
+ }
+ }
+ for (var name in block.use) {
+ if (!(name in inputVars)) {
+ inputVars[name] = 1;
+ var reg = junctionVariables[name].reg;
+ assert(reg !== null, 'input variable doesnt have a register');
+ inputDeadLoc[reg] = block.firstDeadLoc[name];
+ inputVarsByReg[reg] = name;
+ }
+ }
+ assert(setSize(setSub(inputVars, jEnter.live)) == 0);
+ // Scan through backwards, allocating registers on demand.
+ // Be careful to avoid conflicts with the input registers.
// We consume free registers in last-used order, which helps to
// eliminate "x=y" assignments that are the last use of "y".
var assignedRegs = {};
var freeRegsByType = copy(allRegsByType);
- // Begin with all live vars assigned per the entry-point.
- for (var name in liveness[0]) {
+ // Begin with all live vars assigned per the exit junction.
+ for (var name in jExit.live) {
var reg = junctionVariables[name].reg;
- assert(reg !== null, 'input variable doesnt have a register');
+ assert(reg !== null, 'output variable doesnt have a register');
assignedRegs[name] = reg;
delete freeRegsByType[localVars[name]][reg];
}
@@ -3023,66 +3009,71 @@ function registerizeHarder(ast) {
freeRegsByType[j] = keys(freeRegsByType[j]);
}
// Scan through the nodes in sequence, modifying each node in-place
- // and freeing registers according to the calculated liveness info.
- for (var j = 0; j < block.nodes.length; j++) {
+ // and grabbing/freeing registers as needed.
+ var maybeRemoveNodes = [];
+ for (var j = block.nodes.length - 1; j >= 0; j--) {
var node = block.nodes[j];
var name = node[0] === 'assign' ? node[2][1] : node[1];
var allRegs = allRegsByType[localVars[name]];
var freeRegs = freeRegsByType[localVars[name]];
var reg = assignedRegs[name];
if (node[0] === 'name') {
- // A use. It should already be in a register.
- assert(liveness[j][name], 'use node, but name was not alive?')
- assert(reg, 'live variable did not have a reg?')
- node[1] = allRegs[reg];
- } else {
- // A kill. This should assign it a new register.
- assert(!liveness[j][name], 'kill node, but name was alive?')
- assert(!reg, 'non-live variable still had a reg?')
- if (name in jExit.live && j === block.lastKillLoc[name]) {
- // Assignment to an output variable, must use pre-assigned reg.
- reg = junctionVariables[name].reg;
- assignedRegs[name] = reg;
- for (var k = freeRegs.length - 1; k >= 0; k--) {
- if (freeRegs[k] === reg) {
+ // A use. Grab a register if it doesn't have one.
+ if (!reg) {
+ if (name in inputVars && j <= block.firstDeadLoc[name]) {
+ // Assignment to an input variable, must use pre-assigned reg.
+ reg = junctionVariables[name].reg;
+ assignedRegs[name] = reg;
+ for (var k = freeRegs.length - 1; k >= 0; k--) {
+ if (freeRegs[k] === reg) {
+ freeRegs.splice(k, 1);
+ break;
+ }
+ }
+ } else {
+ // Try to use one of the existing free registers.
+ // It must not conflict with an input register.
+ for (var k = freeRegs.length - 1; k >= 0; k--) {
+ reg = freeRegs[k];
+ // Check for conflict with input registers.
+ if (block.firstKillLoc[name] <= inputDeadLoc[reg]) {
+ if (name !== inputVarsByReg[reg]) {
+ continue;
+ }
+ }
+ // Found one!
+ assignedRegs[name] = reg;
freeRegs.splice(k, 1);
break;
}
- }
- } else {
- // Try to use one of the existing free registers.
- // It must not conflict with an output register.
- for (var k = freeRegs.length - 1; k >= 0; k--) {
- reg = freeRegs[k];
- // Check for conflict with output registers.
- if (block.lastUseLoc[name] > outputAssignLoc[reg]) {
- if (name !== outputVars[reg]) {
- continue;
- }
+ // If we didn't find a suitable register, create a new one.
+ if (!assignedRegs[name]) {
+ reg = createReg(name);
+ assignedRegs[name] = reg;
}
- // Found one!
- assignedRegs[name] = reg;
- freeRegs.splice(k, 1);
- break;
- }
- // If we didn't find a suitable register, create a new one.
- if (!assignedRegs[name]) {
- reg = createReg(name);
- assignedRegs[name] = reg;
}
}
- // Modify assignment to use the new name.
- // If we happen to create a "x=x" type do-nothing assignment,
- // we can safely morph it into a no-op.
+ node[1] = allRegs[reg];
+ } else {
+ // A kill. This frees the assigned register.
+ assert(reg, 'live variable doesnt have a reg?')
node[2][1] = allRegs[reg];
- if (node[3][0] === 'name' && node[3][1] === node[2][1]) {
- morphNode(node, ['block', []]);
+ freeRegs.push(reg);
+ delete assignedRegs[name];
+ if (node[3][0] === 'name' && node[3][1] in localVars) {
+ maybeRemoveNodes.push([j, node]);
}
}
- // Free the reg if it's not live in the next step.
- if (!liveness[j+1][name]) {
- delete assignedRegs[name];
- freeRegs.push(reg);
+ }
+ // If we managed to create an "x=x" assignments, remove them.
+ for (var j = 0; j < maybeRemoveNodes.length; j++) {
+ var node = maybeRemoveNodes[j][1];
+ if (node[2][1] === node[3][1]) {
+ if (block.isexpr[maybeRemoveNodes[j][0]]) {
+ morphNode(node, node[2]);
+ } else {
+ morphNode(node, ['block', []]);
+ }
}
}
}
diff --git a/tools/test-js-optimizer-asm-regs-harder-output.js b/tools/test-js-optimizer-asm-regs-harder-output.js
new file mode 100644
index 00000000..448fb67a
--- /dev/null
+++ b/tools/test-js-optimizer-asm-regs-harder-output.js
@@ -0,0 +1,129 @@
+function asm(d1, i2) {
+ d1 = +d1;
+ i2 = i2 | 0;
+ i2 = d1 + d1 | 0;
+ d1 = d(Math_max(10, Math_min(5, f())));
+ i2 = i2 + 2 | 0;
+ print(i2);
+ d1 = d1 * 5;
+ return d1;
+}
+function _doit(i3, i2, i1) {
+ i3 = i3 | 0;
+ i2 = i2 | 0;
+ i1 = i1 | 0;
+ i3 = STACKTOP;
+ _printf(__str | 0, (tempInt = STACKTOP, STACKTOP = STACKTOP + 8 | 0, HEAP32[(tempInt & 16777215) >> 2] = i2, HEAP32[(tempInt + 4 & 16777215) >> 2] = i1, tempInt));
+ STACKTOP = i3;
+ return 0 | 0;
+}
+function stackRestore(i1) {
+ i1 = i1 | 0;
+ STACKTOP = i1;
+}
+function switchey(d1, i2) {
+ d1 = +d1;
+ i2 = i2 | 0;
+ switch (d1 | 0) {
+ case 0:
+ i2 = d1 + d1 | 0;
+ d1 = d(Math_max(10, Math_min(5, f())));
+ i2 = i2 + 2 | 0;
+ print(i2);
+ d1 = d1 * 5;
+ return d1;
+ case 1:
+ return 20;
+ }
+}
+function switchey2() {
+ var i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, d6 = +0, d7 = +0;
+ i2 = STACKTOP;
+ STACKTOP = STACKTOP + 8 | 0;
+ i3 = 1;
+ while (1) switch (i3 | 0) {
+ case 1:
+ i1 = i2 | 0;
+ __ZN6RandomC1Ev(i1);
+ i4 = 0;
+ i5 = 0;
+ i3 = 2;
+ break;
+ case 2:
+ d7 = +__ZN6Random3getEf(8, +1);
+ d6 = +__ZN6Random3getEf(i1, +1);
+ _printf(24, (tempInt = STACKTOP, STACKTOP = STACKTOP + 16 | 0, HEAPF64[CHECK_ALIGN_8(tempInt | 0) >> 3] = d7, HEAPF64[CHECK_ALIGN_8(tempInt + 8 | 0) >> 3] = d6, tempInt) | 0);
+ i5 = (d7 != d6 & 1) + i5 | 0;
+ i4 = i4 + 1 | 0;
+ if ((i4 | 0) < 100) {
+ i3 = 2;
+ break;
+ } else {
+ i3 = 3;
+ break;
+ }
+ case 3:
+ _printf(16, (tempInt = STACKTOP, STACKTOP = STACKTOP + 8 | 0, HEAP32[CHECK_ALIGN_4(tempInt | 0) >> 2] = i5, tempInt) | 0);
+ STACKTOP = i2;
+ return 0;
+ }
+ return 0;
+}
+function iffey() {
+ var i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, d6 = +0, d7 = +0;
+ i2 = STACKTOP;
+ STACKTOP = STACKTOP + 8 | 0;
+ i4 = 1;
+ while (1) {
+ if (i4 | 0) {
+ i1 = i2 | 0;
+ __ZN6RandomC1Ev(i1);
+ i3 = 0;
+ i5 = 0;
+ i4 = 2;
+ } else {
+ d7 = +__ZN6Random3getEf(8, +1);
+ d6 = +__ZN6Random3getEf(i1, +1);
+ _printf(24, (tempInt = STACKTOP, STACKTOP = STACKTOP + 16 | 0, HEAPF64[CHECK_ALIGN_8(tempInt | 0) >> 3] = d7, HEAPF64[CHECK_ALIGN_8(tempInt + 8 | 0) >> 3] = d6, tempInt) | 0);
+ i5 = (d7 != d6 & 1) + i5 | 0;
+ i3 = i3 + 1 | 0;
+ if ((i3 | 0) < 100) {
+ i4 = 2;
+ } else {
+ return 10;
+ }
+ }
+ }
+ return 0;
+}
+function labelledJump(i3) {
+ i3 = i3 | 0;
+ var i1 = 0, i2 = 0;
+ i2 = 2;
+ if (i3) {
+ i2 = 17;
+ i1 = 1;
+ }
+ if (i1 == 1) {
+ i3 = i2 + 1;
+ } else {
+ i3 = i2 + 1;
+ }
+ return i3;
+}
+function linkedVars() {
+ var i1 = 0, i2 = 0;
+ while (1) {
+ i2 = 9;
+ i1 = 5;
+ while (i2 > 0 || i1 > 0) {
+ if (i2 < i1) {
+ i2 = i2 - 1;
+ } else {
+ i1 = i1 - 1;
+ }
+ }
+ }
+ return i2 + i1;
+}
+
diff --git a/tools/test-js-optimizer-asm-regs-harder.js b/tools/test-js-optimizer-asm-regs-harder.js
new file mode 100644
index 00000000..cf44c1dd
--- /dev/null
+++ b/tools/test-js-optimizer-asm-regs-harder.js
@@ -0,0 +1,150 @@
+function asm(x, y) {
+ x = +x;
+ y = y | 0;
+ var int1 = 0, int2 = 0; // do not mix the types!
+ var double1 = +0, double2 = +0;
+ int1 = (x+x)|0;
+ double1 = d(Math_max(10, Math_min(5, f())));
+ int2 = (int1+2)|0;
+ print(int2);
+ double2 = double1*5;
+ return double2;
+}
+function _doit($x, $y$0, $y$1) {
+ $x = $x | 0;
+ $y$0 = $y$0 | 0;
+ $y$1 = $y$1 | 0;
+ var __stackBase__ = 0;
+ __stackBase__ = STACKTOP;
+ _printf(__str | 0, (tempInt = STACKTOP, STACKTOP = STACKTOP + 8 | 0, HEAP32[(tempInt & 16777215) >> 2] = $y$0, HEAP32[(tempInt + 4 & 16777215) >> 2] = $y$1, tempInt));
+ STACKTOP = __stackBase__;
+ return 0 | 0;
+}
+function stackRestore(top) {
+ top = top|0;
+ STACKTOP = top;
+}
+function switchey(x, y) {
+ x = +x;
+ y = y | 0;
+ var int1 = 0, int2 = 0; // do not mix the types!
+ var double1 = +0, double2 = +0;
+ switch(x|0) {
+ case 0:
+ int1 = (x+x)|0;
+ double1 = d(Math_max(10, Math_min(5, f())));
+ int2 = (int1+2)|0;
+ print(int2);
+ double2 = double1*5;
+ return double2;
+ case 1:
+ return 20;
+ }
+}
+function switchey2() {
+ var $rng2 = 0, $count_06 = 0, $i_05 = 0, $2 = +0, $3 = +0, $count_1 = 0, $9 = 0, label = 0, __stackBase__ = 0;
+ __stackBase__ = STACKTOP;
+ STACKTOP = STACKTOP + 8 | 0;
+ label = 1;
+ while (1) switch (label | 0) {
+ case 1:
+ $rng2 = __stackBase__ | 0;
+ __ZN6RandomC1Ev($rng2);
+ $i_05 = 0;
+ $count_06 = 0;
+ label = 2;
+ break;
+ case 2:
+ $2 = +__ZN6Random3getEf(8, +1);
+ $3 = +__ZN6Random3getEf($rng2, +1);
+ _printf(24, (tempInt = STACKTOP, STACKTOP = STACKTOP + 16 | 0, HEAPF64[CHECK_ALIGN_8(tempInt | 0) >> 3] = $2, HEAPF64[CHECK_ALIGN_8(tempInt + 8 | 0) >> 3] = $3, tempInt) | 0);
+ $count_1 = ($2 != $3 & 1) + $count_06 | 0;
+ $9 = $i_05 + 1 | 0;
+ if (($9 | 0) < 100) {
+ $i_05 = $9;
+ $count_06 = $count_1;
+ label = 2;
+ break;
+ } else {
+ label = 3;
+ break;
+ }
+ case 3:
+ _printf(16, (tempInt = STACKTOP, STACKTOP = STACKTOP + 8 | 0, HEAP32[CHECK_ALIGN_4(tempInt | 0) >> 2] = $count_1, tempInt) | 0);
+ STACKTOP = __stackBase__;
+ return 0;
+ }
+ return 0;
+}
+function iffey() {
+ var $rng2 = 0, $count_06 = 0, $i_05 = 0, $2 = +0, $3 = +0, $count_1 = 0, $9 = 0, label = 0, __stackBase__ = 0;
+ __stackBase__ = STACKTOP;
+ STACKTOP = STACKTOP + 8 | 0;
+ label = 1;
+ while (1) {
+ if (label | 0) {
+ $rng2 = __stackBase__ | 0;
+ __ZN6RandomC1Ev($rng2);
+ $i_05 = 0;
+ $count_06 = 0;
+ label = 2;
+ } else {
+ $2 = +__ZN6Random3getEf(8, +1);
+ $3 = +__ZN6Random3getEf($rng2, +1);
+ _printf(24, (tempInt = STACKTOP, STACKTOP = STACKTOP + 16 | 0, HEAPF64[CHECK_ALIGN_8(tempInt | 0) >> 3] = $2, HEAPF64[CHECK_ALIGN_8(tempInt + 8 | 0) >> 3] = $3, tempInt) | 0);
+ $count_1 = ($2 != $3 & 1) + $count_06 | 0;
+ $9 = $i_05 + 1 | 0;
+ if (($9 | 0) < 100) {
+ $i_05 = $9;
+ $count_06 = $count_1;
+ label = 2;
+ } else {
+ label = 3;
+ return 10;
+ }
+ }
+ }
+ return 0;
+}
+function labelledJump(x) {
+ x = x | 0;
+ var label = 0
+ // y and z don't conflict, but only if you know about labelled jumps.
+ var y = 0, z = 0;
+ y = 2;
+ if (x) {
+ z = 17;
+ label = 1;
+ }
+ if (label == 1) {
+ x = z + 1;
+ } else {
+ x = y + 1;
+ }
+ return x;
+}
+function linkedVars() {
+ var outer1 = 0, outer2 = 0;
+ var inner1_0 = 0, inner1_1 = 0, inner2_0 = 0, inner2_1 = 0;
+ while (1) {
+ outer1 = 9;
+ outer2 = 5;
+ while (outer1 > 0 || outer2 > 0) {
+ // All these copy assignment should be eliminated by var sharing.
+ inner1_0 = outer1;
+ inner2_0 = outer2;
+ if (inner1_0 < inner2_0) {
+ inner1_1 = inner1_0 - 1;
+ inner2_1 = inner2_0;
+ } else {
+ inner1_1 = inner1_0;
+ inner2_1 = inner2_0 - 1;
+ }
+ outer1 = inner1_1;
+ outer2 = inner2_1;
+ }
+ }
+ return outer1 + outer2;
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "_doit", "stackRestore", "switchey", "switchey2", "iffey", "labelledJump", "linkedVars']
+