diff options
-rw-r--r-- | AUTHORS | 5 | ||||
-rwxr-xr-x | emcc | 81 | ||||
-rw-r--r-- | src/compiler.js | 8 | ||||
-rw-r--r-- | src/corruptionCheck.js | 98 | ||||
-rw-r--r-- | src/jsifier.js | 22 | ||||
-rw-r--r-- | src/library.js | 42 | ||||
-rw-r--r-- | src/library_browser.js | 2 | ||||
-rw-r--r-- | src/library_egl.js | 41 | ||||
-rw-r--r-- | src/library_gl.js | 166 | ||||
-rw-r--r-- | src/library_glut.js | 6 | ||||
-rw-r--r-- | src/library_sdl.js | 6 | ||||
-rw-r--r-- | src/parseTools.js | 16 | ||||
-rw-r--r-- | src/preamble.js | 156 | ||||
-rw-r--r-- | src/runtime.js | 27 | ||||
-rw-r--r-- | src/settings.js | 23 | ||||
-rw-r--r-- | system/lib/libcxx/symbols | 9 | ||||
-rw-r--r-- | system/lib/libcxxabi/symbols | 7 | ||||
-rwxr-xr-x | tests/csmith_driver.py | 50 | ||||
-rw-r--r-- | tests/cubegeom.c | 14 | ||||
-rw-r--r-- | tests/cubegeom_pre_vao.c | 333 | ||||
-rw-r--r-- | tests/gl_ps_workaround2.c | 230 | ||||
-rwxr-xr-x | tests/runner.py | 310 | ||||
-rw-r--r-- | tools/file_packager.py | 2 | ||||
-rw-r--r-- | tools/js-optimizer.js | 2 | ||||
-rw-r--r-- | tools/shared.py | 1 | ||||
-rw-r--r-- | tools/test-js-optimizer-regs-output.js | 4 | ||||
-rw-r--r-- | tools/test-js-optimizer-regs.js | 6 |
27 files changed, 1313 insertions, 354 deletions
@@ -48,6 +48,5 @@ a license to everyone to use it as detailed in LICENSE.) * Jasper St. Pierre <jstpierre@mecheye.net> * Manuel Schölling <manuel.schoelling@gmx.de> * Bruce Mitchener, Jr. <bruce.mitchener@gmail.com> - - - +* Michael Bishop <mbtyke@gmail.com> +* Roger Braun <roger@rogerbraun.net> @@ -119,11 +119,20 @@ if len(sys.argv) == 1: exit(1) if sys.argv[1] == '--version': - print '''emcc (Emscripten GCC-like replacement) 2.0 -Copyright (C) 2012 the Emscripten authors. + revision = '(unknown revision)' + here = os.getcwd() + os.chdir(shared.path_from_root()) + try: + revision = execute(['git', 'show'], stdout=PIPE, stderr=PIPE)[0].split('\n')[0] + except: + pass + finally: + os.chdir(here) + print '''emcc (Emscripten GCC-like replacement) %s (%s) +Copyright (C) 2013 the Emscripten authors (see AUTHORS.txt) This is free and open source software under the MIT license. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - ''' + ''' % (shared.EMSCRIPTEN_VERSION, revision) exit(0) elif sys.argv[1] == '--help': this = os.path.basename('em++' if os.environ.get('EMMAKEN_CXX') else 'emcc') @@ -148,14 +157,19 @@ Options that are modified or new in %s include: compiling to JavaScript, not to intermediate bitcode. -O2 As -O1, plus the relooper (loop recreation), - plus closure compiler advanced opts, plus - LLVM -O2 optimizations - Warning: Compiling with this takes a long time! + plus LLVM -O2 optimizations -O3 As -O2, plus dangerous optimizations that may - break the generated code! This is not - recommended at all, see the wiki for more - details (you can try -O2 and then add - dangerous optimizations one by one). + break the generated code! This adds + + -s INLINING_LIMIT=0 + -s DOUBLE_MODE=0 + -s PRECISE_I64_MATH=0 + --closure 1 + + This is not recommended at all. A better idea + is to try each of these separately on top of + -O2 to see what works. See the wiki for more + information. -s OPTION=VALUE JavaScript code generation option passed into the emscripten compiler. For the @@ -177,6 +191,12 @@ Options that are modified or new in %s include: the last compilation phase from bitcode to JavaScript, or else we will remove it by default in -O1 and above. + In -O0, line numbers wil be shown in the + generated code. In -O1 and above, the optimizer + removes those comments. This flag does however + have the effect of disabling anything that + causes name mangling or minification (closure + or the registerize pass). --typed-arrays <mode> 0: No typed arrays 1: Parallel typed arrays @@ -193,8 +213,17 @@ Options that are modified or new in %s include: (see --llvm-opts), setting this to 1 has no effect. - --closure <on> 0: No closure compiler (default in -O0, -O1) - 1: Run closure compiler (default in -O2, -O3) + --closure <on> 0: No closure compiler (default in -O2 and below) + 1: Run closure compiler. This greatly reduces + code size and may in some cases increase + runtime speed (although the opposite can also + occur). Note that it takes time to run, and + may require some changes to the code. This + is run by default in -O3. + + Note: If closure compiler hits an out-of-memory, + try adjusting JAVA_HEAP_SIZE in the environment + (for example, to 4096m for 4GB). --js-transform <cmd> <cmd> will be called on the generated code before it is optimized. This lets you modify @@ -600,7 +629,8 @@ try: ignore_dynamic_linking = False shell_path = shared.path_from_root('src', 'shell.html') js_libraries = [] - keep_debug = False + keep_llvm_debug = False + keep_js_debug = False bind = False jcache = False if use_cxx: @@ -667,7 +697,8 @@ try: newargs[i] = '' newargs[i+1] = '' elif newargs[i] == '-g': - keep_debug = True + keep_llvm_debug = True + keep_js_debug = True elif newargs[i] == '--bind': bind = True newargs[i] = '' @@ -738,8 +769,9 @@ try: if llvm_opts is None: llvm_opts = LLVM_OPT_LEVEL[opt_level] if llvm_lto is None: llvm_lto = llvm_opts > 0 - if closure is None: closure = 1 if opt_level >= 2 else 0 - if opt_level <= 0: keep_debug = True # always keep debug in -O0 + if opt_level <= 0: keep_llvm_debug = keep_js_debug = True # always keep debug in -O0 + if opt_level > 0: keep_llvm_debug = False # JS optimizer wipes out llvm debug info from being visible + if closure is None and opt_level == 3: closure = True if DEBUG: start_time = time.time() # done after parsing arguments, which might affect debug state @@ -879,7 +911,11 @@ try: shared.Settings.CORRECT_OVERFLOWS = 1 if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2: - keep_debug = True # must keep debug info to do line-by-line operations + keep_llvm_debug = True # must keep debug info to do line-by-line operations + + if (keep_llvm_debug or keep_js_debug) and closure: + print >> sys.stderr, 'emcc: warning: disabling closure because debug info was requested' + closure = False if minify_whitespace is None: minify_whitespace = closure # if closure is run, minify whitespace @@ -984,7 +1020,7 @@ try: def create_libcxx(): if DEBUG: print >> sys.stderr, 'emcc: building libcxx for cache' os = [] - for src in ['algorithm.cpp', 'condition_variable.cpp', 'future.cpp', 'iostream.cpp', 'memory.cpp', 'random.cpp', 'stdexcept.cpp', 'system_error.cpp', 'utility.cpp', 'bind.cpp', 'debug.cpp', 'hash.cpp', 'mutex.cpp', 'string.cpp', 'thread.cpp', 'valarray.cpp', 'chrono.cpp', 'exception.cpp', 'ios.cpp', 'locale.cpp', 'regex.cpp', 'strstream.cpp', 'typeinfo.cpp']: + for src in ['algorithm.cpp', 'condition_variable.cpp', 'future.cpp', 'iostream.cpp', 'memory.cpp', 'random.cpp', 'stdexcept.cpp', 'system_error.cpp', 'utility.cpp', 'bind.cpp', 'debug.cpp', 'hash.cpp', 'mutex.cpp', 'string.cpp', 'thread.cpp', 'valarray.cpp', 'chrono.cpp', 'exception.cpp', 'ios.cpp', 'locale.cpp', 'regex.cpp', 'strstream.cpp']: o = in_temp(src + '.o') execute([shared.PYTHON, shared.EMXX, shared.path_from_root('system', 'lib', 'libcxx', src), '-o', o], stdout=stdout, stderr=stderr) os.append(o) @@ -1003,7 +1039,7 @@ try: def create_libcxxabi(): if DEBUG: print >> sys.stderr, 'emcc: building libcxxabi for cache' os = [] - for src in ['private_typeinfo.cpp']: + for src in ['private_typeinfo.cpp', 'typeinfo.cpp']: o = in_temp(src + '.o') execute([shared.PYTHON, shared.EMXX, shared.path_from_root('system', 'lib', 'libcxxabi', 'src', src), '-o', o], stdout=stdout, stderr=stderr) os.append(o) @@ -1084,7 +1120,7 @@ try: # Optimize, if asked to if not LEAVE_INPUTS_RAW: - link_opts = [] if keep_debug else ['-strip-debug'] + link_opts = [] if keep_llvm_debug else ['-strip-debug'] # remove LLVM debug info in -O1+, since the optimizer removes it anyhow if llvm_opts > 0: shared.Building.llvm_opt(in_temp(target_basename + '.bc'), llvm_opts) if DEBUG: save_intermediate('opt', 'bc') @@ -1216,8 +1252,9 @@ try: if DEBUG: print >> sys.stderr, 'emcc: running closure' final = shared.Building.closure_compiler(final) if DEBUG: save_intermediate('closure') - elif shared.Settings.ASM_JS and shared.Settings.RELOOP: - js_optimizer_queue += ['registerize'] # we can't use closure in asm, but this does much of the same + elif shared.Settings.RELOOP and not closure and not keep_js_debug: + # do this if closure is not enabled (it gives similar speedups), and we do not need to keep debug info around + js_optimizer_queue += ['registerize'] if opt_level >= 1: if DEBUG: print >> sys.stderr, 'emcc: running post-closure post-opts' diff --git a/src/compiler.js b/src/compiler.js index 25c306cf..0b43842e 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -160,12 +160,6 @@ if (SAFE_HEAP >= 2) { SAFE_HEAP_LINES = set(SAFE_HEAP_LINES); // for fast checking } -if (PGO) { // by default, correct everything during PGO - CORRECT_SIGNS = CORRECT_SIGNS || 1; - CORRECT_OVERFLOWS = CORRECT_OVERFLOWS || 1; - CORRECT_ROUNDINGS = CORRECT_ROUNDINGS || 1; -} - EXPORTED_FUNCTIONS = set(EXPORTED_FUNCTIONS); EXPORTED_GLOBALS = set(EXPORTED_GLOBALS); EXCEPTION_CATCHING_WHITELIST = set(EXCEPTION_CATCHING_WHITELIST); @@ -185,7 +179,7 @@ assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB)); // shared libraries must have if (phase == 'pre') { if (!MICRO_OPTS || !RELOOP || ASSERTIONS || CHECK_SIGNS || CHECK_OVERFLOWS || INIT_STACK || INIT_HEAP || - !SKIP_STACK_IN_SMALL || SAFE_HEAP || PGO || PROFILE || !DISABLE_EXCEPTION_CATCHING) { + !SKIP_STACK_IN_SMALL || SAFE_HEAP || !DISABLE_EXCEPTION_CATCHING) { print('// Note: Some Emscripten settings will significantly limit the speed of the generated code.'); } else { print('// Note: For maximum-speed code, see "Optimizing Code" on the Emscripten wiki, https://github.com/kripken/emscripten/wiki/Optimizing-Code'); diff --git a/src/corruptionCheck.js b/src/corruptionCheck.js new file mode 100644 index 00000000..315f5cf0 --- /dev/null +++ b/src/corruptionCheck.js @@ -0,0 +1,98 @@ + +// See settings.js, CORRUPTION_CHECK + +var CorruptionChecker = { + BUFFER_FACTOR: Math.round({{{ CORRUPTION_CHECK }}}), + + ptrs: {}, + checks: 0, + checkFrequency: 1, + + init: function() { + this.realMalloc = _malloc; + _malloc = Module['_malloc'] = this.malloc; + + this.realFree = _free; + _free = Module['_free'] = this.free; + + if (typeof _realloc != 'undefined') { + this.realRealloc = _realloc; + _realloc = Module['_realloc'] = this.realloc; + } + + __ATEXIT__.push({ func: function() { + Module.printErr('No corruption detected, ran ' + CorruptionChecker.checks + ' checks.'); + } }); + }, + malloc: function(size) { + if (size <= 0) size = 1; // malloc(0) sometimes happens - just allocate a larger area, no harm + CorruptionChecker.checkAll(); + size = (size+7)&(~7); + var allocation = CorruptionChecker.realMalloc(size*(1+2*CorruptionChecker.BUFFER_FACTOR)); + var ptr = allocation + size*CorruptionChecker.BUFFER_FACTOR; + assert(!CorruptionChecker.ptrs[ptr]); + CorruptionChecker.ptrs[ptr] = size; + CorruptionChecker.fillBuffer(allocation, size*CorruptionChecker.BUFFER_FACTOR); + CorruptionChecker.fillBuffer(allocation + size*(1+CorruptionChecker.BUFFER_FACTOR), size*CorruptionChecker.BUFFER_FACTOR); + //Module.printErr('malloc ' + size + ' ==> ' + [ptr, allocation]); + return ptr; + }, + free: function(ptr) { + if (!ptr) return; // ok to free(NULL), does nothing + CorruptionChecker.checkAll(); + var size = CorruptionChecker.ptrs[ptr]; + //Module.printErr('free ' + ptr + ' of size ' + size); + assert(size); + var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR; + //Module.printErr('free ' + ptr + ' of size ' + size + ' and allocation ' + allocation); + delete CorruptionChecker.ptrs[ptr]; + CorruptionChecker.realFree(allocation); + }, + realloc: function(ptr, newSize) { + //Module.printErr('realloc ' + ptr + ' to size ' + newSize); + if (newSize <= 0) newSize = 1; // like in malloc + if (!ptr) return CorruptionChecker.malloc(newSize); // realloc(NULL, size) forwards to malloc according to the spec + var size = CorruptionChecker.ptrs[ptr]; + assert(size); + var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR; + var newPtr = CorruptionChecker.malloc(newSize); + //Module.printErr('realloc ' + ptr + ' to size ' + newSize + ' is now ' + newPtr); + var newAllocation = newPtr + newSize*CorruptionChecker.BUFFER_FACTOR; + HEAPU8.set(HEAPU8.subarray(ptr, ptr + Math.min(size, newSize)), newPtr); + CorruptionChecker.free(ptr); + return newPtr; + }, + canary: function(x) { + return (x&127) + 10; + }, + fillBuffer: function(buffer, size) { + for (var x = buffer; x < buffer + size; x++) { + {{{ makeSetValue('x', 0, 'CorruptionChecker.canary(x)', 'i8') }}}; + } + }, + checkBuffer: function(buffer, size) { + for (var x = buffer; x < buffer + size; x++) { + if (({{{ makeGetValue('x', 0, 'i8') }}}&255) != CorruptionChecker.canary(x)) { + assert(0, 'Heap corruption detected!' + [x, buffer, size, {{{ makeGetValue('x', 0, 'i8') }}}&255, CorruptionChecker.canary(x)]); + } + } + }, + checkPtr: function(ptr) { + var size = CorruptionChecker.ptrs[ptr]; + assert(size); + var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR; + CorruptionChecker.checkBuffer(allocation, size*CorruptionChecker.BUFFER_FACTOR); + CorruptionChecker.checkBuffer(allocation + size*(1+CorruptionChecker.BUFFER_FACTOR), size*CorruptionChecker.BUFFER_FACTOR); + }, + checkAll: function(force) { + CorruptionChecker.checks++; + if (!force && CorruptionChecker.checks % CorruptionChecker.checkFrequency != 0) return; + //Module.printErr('checking for corruption ' + (CorruptionChecker.checks/CorruptionChecker.checkFrequency)); + for (var ptr in CorruptionChecker.ptrs) { + CorruptionChecker.checkPtr(ptr, false); + } + }, +}; + +CorruptionChecker.init(); + diff --git a/src/jsifier.js b/src/jsifier.js index 761a5fec..71975b9d 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -478,8 +478,7 @@ function JSify(data, functionsOnly, givenFunctions) { ident = '_' + ident; } var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : ''); - // redirected idents just need a var, but no value assigned to them - it would be unused - var contentText = isFunction ? snippet : ('var ' + ident + (redirectedIdent ? '' : '=' + snippet) + ';'); + var contentText = isFunction ? snippet : ('var ' + ident + '=' + snippet + ';'); if (ASM_JS) { var sig = LibraryManager.library[ident.substr(1) + '__sig']; if (isFunction && sig && LibraryManager.library[ident.substr(1) + '__asm']) { @@ -631,15 +630,6 @@ function JSify(data, functionsOnly, givenFunctions) { } } - if (PROFILE) { - func.JS += ' if (PROFILING) { ' - + 'var __parentProfilingNode__ = PROFILING_NODE; PROFILING_NODE = PROFILING_NODE.children["' + func.ident + '"]; ' - + 'if (!PROFILING_NODE) __parentProfilingNode__.children["' + func.ident + '"] = PROFILING_NODE = { time: 0, children: {}, calls: 0 };' - + 'PROFILING_NODE.calls++; ' - + 'var __profilingStartTime__ = Date.now() ' - + '}\n'; - } - if (true) { // TODO: optimize away when not needed if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */'; func.JS += ' var label = 0;\n'; @@ -1145,12 +1135,6 @@ function JSify(data, functionsOnly, givenFunctions) { }); makeFuncLineActor('return', function(item) { var ret = RuntimeGenerator.stackExit(item.funcData.initialStack, item.funcData.otherStackAllocations) + ';\n'; - if (PROFILE) { - ret += 'if (PROFILING) { ' - + 'PROFILING_NODE.time += Date.now() - __profilingStartTime__; ' - + 'PROFILING_NODE = __parentProfilingNode__ ' - + '}\n'; - } if (LABEL_DEBUG && functionNameFilterTest(item.funcData.ident)) { ret += "Module.print(INDENT + 'Exiting: " + item.funcData.ident + "');\n" + "INDENT = INDENT.substr(0, INDENT.length-2);\n"; @@ -1547,6 +1531,10 @@ function JSify(data, functionsOnly, givenFunctions) { // This is the main 'post' pass. Print out the generated code that we have here, together with the // rest of the output that we started to print out earlier (see comment on the // "Final shape that will be created"). + if (CORRUPTION_CHECK) { + assert(!ASM_JS); // cannot monkeypatch asm! + print(processMacros(read('corruptionCheck.js'))); + } if (PRECISE_I64_MATH && Types.preciseI64MathUsed) { print(read('long.js')); } else { diff --git a/src/library.js b/src/library.js index ee0befa1..d8f98d73 100644 --- a/src/library.js +++ b/src/library.js @@ -2491,6 +2491,17 @@ LibraryManager.library = { continue; } + // TODO: Support strings like "%5c" etc. + if (format[formatIndex] === '%' && format[formatIndex+1] == 'c') { + var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; + argIndex += Runtime.getNativeFieldSize('void*'); + fields++; + next = get(); + {{{ makeSetValue('argPtr', 0, 'next', 'i8') }}} + formatIndex += 2; + continue; + } + // remove whitespace while (1) { next = get(); @@ -5080,6 +5091,7 @@ LibraryManager.library = { }, __cxa_call_unexpected: function(exception) { + Module.printErr('Unexpected exception thrown, this is not properly supported - aborting'); ABORT = true; throw exception; }, @@ -7303,36 +7315,6 @@ LibraryManager.library = { emscripten_random: function() { return Math.random(); }, - - $Profiling: { - max_: 0, - times: null, - invalid: 0, - dump: function() { - if (Profiling.invalid) { - Module.printErr('Invalid # of calls to Profiling begin and end!'); - return; - } - Module.printErr('Profiling data:') - for (var i = 0; i < Profiling.max_; i++) { - Module.printErr('Block ' + i + ': ' + Profiling.times[i]); - } - } - }, - EMSCRIPTEN_PROFILE_INIT__deps: ['$Profiling'], - EMSCRIPTEN_PROFILE_INIT: function(max_) { - Profiling.max_ = max_; - Profiling.times = new Array(max_); - for (var i = 0; i < max_; i++) Profiling.times[i] = 0; - }, - EMSCRIPTEN_PROFILE_BEGIN__inline: function(id) { - return 'Profiling.times[' + id + '] -= Date.now();' - + 'Profiling.invalid++;' - }, - EMSCRIPTEN_PROFILE_END__inline: function(id) { - return 'Profiling.times[' + id + '] += Date.now();' - + 'Profiling.invalid--;' - } }; function autoAddDeps(object, name) { diff --git a/src/library_browser.js b/src/library_browser.js index 6af2ce0b..e9396d69 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -268,7 +268,7 @@ mergeInto(LibraryManager.library, { } return ctx; }, - + destroyContext: function(canvas, useWebGL, setInModule) {}, requestFullScreen: function() { var canvas = Module['canvas']; function fullScreenChange() { diff --git a/src/library_egl.js b/src/library_egl.js index a9eb37dd..271ea29e 100644 --- a/src/library_egl.js +++ b/src/library_egl.js @@ -83,6 +83,17 @@ var LibraryEGL = { return 1; }, +// EGLAPI EGLBoolean EGLAPIENTRY eglTerminate(EGLDisplay dpy); + eglTerminate: function(display) { + if (display != 62000 /* Magic ID for Emscripten 'default display' */) { + EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); + return 0; + } + // TODO: Tear down EGL here. Currently a no-op since we don't need to actually do anything here for the browser. + EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); + return 1; + }, + // EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config); eglGetConfigs: function(display, configs, config_size, numConfigs) { return EGL.chooseConfig(display, 0, configs, config_size, numConfigs); @@ -225,6 +236,20 @@ var LibraryEGL = { return 62006; /* Magic ID for Emscripten 'default surface' */ }, + // EGLAPI EGLBoolean EGLAPIENTRY eglDestroySurface(EGLDisplay display, EGLSurface surface); + eglDestroySurface: function(display, surface) { + if (display != 62000 /* Magic ID for Emscripten 'default display' */) { + EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); + return 0; + } + if (surface != 62006 /* Magic ID for the only EGLSurface supported by Emscripten */) { + EGL.setErrorCode(0x300D /* EGL_BAD_SURFACE */); + return 1; + } + EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); + return 1; /* Magic ID for Emscripten 'default surface' */ + }, + eglCreateContext__deps: ['glutCreateWindow', '$GL'], // EGLAPI EGLContext EGLAPIENTRY eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list); @@ -234,7 +259,21 @@ var LibraryEGL = { return 0; } - _glutCreateWindow(); + EGL.windowID = _glutCreateWindow(); + EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); + return 62004; // Magic ID for Emscripten EGLContext + }, + + eglDestroyContext__deps: ['glutDestroyWindow', '$GL'], + + // EGLAPI EGLBoolean EGLAPIENTRY eglDestroyContext(EGLDisplay dpy, EGLContext context); + eglDestroyContext: function(display, context) { + if (display != 62000 /* Magic ID for Emscripten 'default display' */) { + EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); + return 0; + } + + _glutDestroyWindow(EGL.windowID); EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); return 62004; // Magic ID for Emscripten EGLContext }, diff --git a/src/library_gl.js b/src/library_gl.js index 1f664717..2c3be61c 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -993,6 +993,10 @@ var LibraryGL = { fogMode: 0x0800, // GL_EXP fogEnabled: false, + // VAO support + vaos: [], + currentVao: null, + init: function() { GLEmulation.fogColor = new Float32Array(4); @@ -1018,6 +1022,11 @@ var LibraryGL = { if (cap == 0x0B60 /* GL_FOG */) { GLEmulation.fogEnabled = true; return; + } else if (cap == 0x0de1 /* GL_TEXTURE_2D */) { + // XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support + // it by forwarding to glEnableClientState + _glEnableClientState(cap); + return; } else if (!(cap in validCapabilities)) { return; } @@ -1028,6 +1037,11 @@ var LibraryGL = { if (cap == 0x0B60 /* GL_FOG */) { GLEmulation.fogEnabled = false; return; + } else if (cap == 0x0de1 /* GL_TEXTURE_2D */) { + // XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support + // it by forwarding to glDisableClientState + _glDisableClientState(cap); + return; } else if (!(cap in validCapabilities)) { return; } @@ -1062,6 +1076,51 @@ var LibraryGL = { return; } case 0x8871: pname = Module.ctx.MAX_COMBINED_TEXTURE_IMAGE_UNITS /* close enough */; break; // GL_MAX_TEXTURE_COORDS + case 0x807A: { // GL_VERTEX_ARRAY_SIZE + var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}}; + return; + } + case 0x807B: { // GL_VERTEX_ARRAY_TYPE + var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}}; + return; + } + case 0x807C: { // GL_VERTEX_ARRAY_STRIDE + var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}}; + return; + } + case 0x8081: { // GL_COLOR_ARRAY_SIZE + var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}}; + return; + } + case 0x8082: { // GL_COLOR_ARRAY_TYPE + var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}}; + return; + } + case 0x8083: { // GL_COLOR_ARRAY_STRIDE + var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}}; + return; + } + case 0x8088: { // GL_TEXTURE_COORD_ARRAY_SIZE + var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}}; + return; + } + case 0x8089: { // GL_TEXTURE_COORD_ARRAY_TYPE + var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}}; + return; + } + case 0x808A: { // GL_TEXTURE_COORD_ARRAY_STRIDE + var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}}; + return; + } } glGetIntegerv(pname, params); }; @@ -1277,8 +1336,13 @@ var LibraryGL = { glBindBuffer(target, buffer); if (target == Module.ctx.ARRAY_BUFFER) { GL.currArrayBuffer = buffer; + if (GLEmulation.currentVao) { + assert(GLEmulation.currentVao.arrayBuffer == buffer || GLEmulation.currentVao.arrayBuffer == 0 || buffer == 0, 'TODO: support for multiple array buffers in vao'); + GLEmulation.currentVao.arrayBuffer = buffer; + } } else if (target == Module.ctx.ELEMENT_ARRAY_BUFFER) { GL.currElementArrayBuffer = buffer; + if (GLEmulation.currentVao) GLEmulation.currentVao.elementArrayBuffer = buffer; } }; @@ -1322,6 +1386,26 @@ var LibraryGL = { } glHint(target, mode); }; + + var glEnableVertexAttribArray = _glEnableVertexAttribArray; + _glEnableVertexAttribArray = function(index) { + glEnableVertexAttribArray(index); + if (GLEmulation.currentVao) GLEmulation.currentVao.enabledVertexAttribArrays[index] = 1; + }; + + var glDisableVertexAttribArray = _glDisableVertexAttribArray; + _glDisableVertexAttribArray = function(index) { + glDisableVertexAttribArray(index); + if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledVertexAttribArrays[index]; + }; + + var glVertexAttribPointer = _glVertexAttribPointer; + _glVertexAttribPointer = function(index, size, type, normalized, stride, pointer) { + glVertexAttribPointer(index, size, type, normalized, stride, pointer); + if (GLEmulation.currentVao) { // TODO: avoid object creation here? likely not hot though + GLEmulation.currentVao.vertexAttribPointers[index] = [index, size, type, normalized, stride, pointer]; + } + }; }, getProcAddress: function(name) { @@ -1437,6 +1521,20 @@ var LibraryGL = { assert(id == 0); }, + glGetPointerv: function(name, p) { + var attribute; + switch(name) { + case 0x808E: // GL_VERTEX_ARRAY_POINTER + attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX]; break; + case 0x8090: // GL_COLOR_ARRAY_POINTER + attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; break; + case 0x8092: // GL_TEXTURE_COORD_ARRAY_POINTER + attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0]; break; + default: throw 'TODO: glGetPointerv for ' + name; + } + {{{ makeSetValue('p', '0', 'attribute ? attribute.pointer : 0', 'i32') }}}; + }, + // GL Immediate mode $GLImmediate__postset: 'GL.immediate.setupFuncs(); Browser.moduleContextCreatedCallbacks.push(function() { GL.immediate.init() });', @@ -2400,6 +2498,54 @@ var LibraryGL = { GL.immediate.clientActiveTexture = texture - 0x84C0; // GL_TEXTURE0 }, + // Vertex array object (VAO) support. TODO: when the WebGL extension is popular, use that and remove this code and GL.vaos + glGenVertexArrays__deps: ['$GLEMulation'], + glGenVertexArrays: function(n, vaos) { + for (var i = 0; i < n; i++) { + var id = GL.getNewId(GLEmulation.vaos); + GLEmulation.vaos[id] = { + id: id, + arrayBuffer: 0, + elementArrayBuffer: 0, + enabledVertexAttribArrays: {}, + vertexAttribPointers: {}, + }; + {{{ makeSetValue('vaos', 'i*4', 'id', 'i32') }}}; + } + }, + glDeleteVertexArrays: function(n, vaos) { + for (var i = 0; i < n; i++) { + var id = {{{ makeGetValue('vaos', 'i*4', 'i32') }}}; + GLEmulation.vaos[id] = null; + if (GLEmulation.currentVao && GLEmulation.currentVao.id == id) GLEmulation.currentVao = null; + } + }, + glBindVertexArray: function(vao) { + if (vao) { + // replay vao + if (GLEmulation.currentVao) _glBindVertexArray(0); // flush the old one out + var info = GLEmulation.vaos[vao]; + _glBindBuffer(Module.ctx.ARRAY_BUFFER, info.arrayBuffer); // XXX overwrite current binding? + _glBindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, info.elementArrayBuffer); + for (var vaa in info.enabledVertexAttribArrays) { + _glEnableVertexAttribArray(vaa); + } + for (var vaa in info.vertexAttribPointers) { + _glVertexAttribPointer.apply(null, info.vertexAttribPointers[vaa]); + } + GLEmulation.currentVao = info; // set currentVao last, so the commands we ran here were not recorded + } else if (GLEmulation.currentVao) { + // undo vao + var info = GLEmulation.currentVao; + GLEmulation.currentVao = null; // set currentVao first, so the commands we run here are not recorded + _glBindBuffer(Module.ctx.ARRAY_BUFFER, 0); // XXX if one was there before we were bound? + _glBindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, 0); + for (var vaa in info.enabledVertexAttribArrays) { + _glDisableVertexAttribArray(vaa); + } + } + }, + // OpenGL Immediate Mode matrix routines. // Note that in the future we might make these available only in certain modes. glMatrixMode__deps: ['$GL', '$GLImmediateSetup', '$GLEmulation'], // emulation is not strictly needed, this is a workaround @@ -2490,6 +2636,7 @@ var LibraryGL = { GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix], GL.immediate.matrix.lib.mat4.frustum(left, right, bottom, top_, nearVal, farVal)); }, + glFrustumf: 'glFrustum', glOrtho: function(left, right, bottom, top_, nearVal, farVal) { GL.immediate.matricesModified = true; @@ -2591,8 +2738,8 @@ var LibraryGL = { glTexGeni: function() { throw 'glTexGeni: TODO' }, glTexGenfv: function() { throw 'glTexGenfv: TODO' }, - glTexEnvi: function() { throw 'glTexEnvi: TODO' }, - glTexEnvfv: function() { throw 'glTexEnvfv: TODO' }, + glTexEnvi: function() { Runtime.warnOnce('glTexEnvi: TODO') }, + glTexEnvfv: function() { Runtime.warnOnce('glTexEnvfv: TODO') }, glTexImage1D: function() { throw 'glTexImage1D: TODO' }, glTexCoord3f: function() { throw 'glTexCoord3f: TODO' }, @@ -2605,6 +2752,21 @@ var LibraryGL = { glVertexAttribPointer__sig: 'viiiiii', glCheckFramebufferStatus__sig: 'ii', glRenderbufferStorage__sig: 'viiii', + + // Open GLES1.1 compatibility + glGenFramebuffersOES : 'glGenFramebuffers', + glGenRenderbuffersOES : 'glGenRenderbuffers', + glBindFramebufferOES : 'glBindFramebuffer', + glBindRenderbufferOES : 'glBindRenderbuffer', + glGetRenderbufferParameterivOES : 'glGetRenderbufferParameteriv', + glFramebufferRenderbufferOES : 'glFramebufferRenderbuffer', + glRenderbufferStorageOES : 'glRenderbufferStorage', + glCheckFramebufferStatusOES : 'glCheckFramebufferStatus', + glDeleteFramebuffersOES : 'glDeleteFramebuffers', + glDeleteRenderbuffersOES : 'glDeleteRenderbuffers', + glGenVertexArraysOES: 'glGenVertexArrays', + glDeleteVertexArraysOES: 'glDeleteVertexArrays', + glBindVertexArrayOES: 'glBindVertexArray', }; // Simple pass-through functions. Starred ones have return values. [X] ones have X in the C name but not in the JS name diff --git a/src/library_glut.js b/src/library_glut.js index 2e662698..bb4dfefa 100644 --- a/src/library_glut.js +++ b/src/library_glut.js @@ -381,6 +381,12 @@ var LibraryGLUT = { return 1; }, + glutDestroyWindow__deps: ['$Browser'], + glutDestroyWindow: function(name) { + Module.ctx = Browser.destroyContext(Module['canvas'], true, true); + return 1; + }, + glutReshapeWindow__deps: ['$GLUT', 'glutPostRedisplay'], glutReshapeWindow: function(width, height) { GLUT.cancelFullScreen(); diff --git a/src/library_sdl.js b/src/library_sdl.js index 4ad7a9a9..e02e1e62 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -1079,7 +1079,9 @@ var LibrarySDL = { } var surf = SDL.makeSurface(raw.width, raw.height, 0, false, 'load:' + filename); var surfData = SDL.surfaces[surf]; + surfData.ctx.globalCompositeOperation = "copy"; surfData.ctx.drawImage(raw, 0, 0, raw.width, raw.height, 0, 0, raw.width, raw.height); + surfData.ctx.globalCompositeOperation = "source-over"; // XXX SDL does not specify that loaded images must have available pixel data, in fact // there are cases where you just want to blit them, so you just need the hardware // accelerated version. However, code everywhere seems to assume that the pixels @@ -1178,6 +1180,10 @@ var LibrarySDL = { // SDL Mixer + Mix_Init: function(flags) { + return 8; /* MIX_INIT_OGG */ + }, + Mix_OpenAudio: function(frequency, format, channels, chunksize) { SDL.allocateChannels(32); // Just record the values for a later call to Mix_QuickLoad_RAW diff --git a/src/parseTools.js b/src/parseTools.js index e081d0de..ca9ad40a 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -976,12 +976,6 @@ function checkSafeHeap() { return SAFE_HEAP === 1 || checkSpecificSafeHeap(); } -if (ASM_JS) { - var hexMemoryMask = '0x' + (TOTAL_MEMORY-1).toString(16); - var decMemoryMask = (TOTAL_MEMORY-1).toString(); - var memoryMask = hexMemoryMask.length <= decMemoryMask.length ? hexMemoryMask : decMemoryMask; -} - function getHeapOffset(offset, type, forceAsm) { if (USE_TYPED_ARRAYS !== 2) { return offset; @@ -991,7 +985,6 @@ function getHeapOffset(offset, type, forceAsm) { } var shifts = Math.log(Runtime.getNativeTypeSize(type))/Math.LN2; offset = '(' + offset + ')'; - if (ASM_JS && (phase == 'funcs' || forceAsm)) offset = '(' + offset + '&' + memoryMask + ')'; if (shifts != 0) { return '(' + offset + '>>' + shifts + ')'; } else { @@ -1070,7 +1063,6 @@ function makeGetTempDouble(i, type, forSet) { // get an aliased part of the temp var ptr = getFastValue('tempDoublePtr', '+', Runtime.getNativeTypeSize(type)*i); var offset; if (type == 'double') { - if (ASM_JS) ptr = '(' + ptr + ')&' + memoryMask; offset = '(' + ptr + ')>>3'; } else { offset = getHeapOffset(ptr, type); @@ -1717,9 +1709,7 @@ function handleOverflow(text, bits) { if (!bits) return text; var correct = correctOverflows(); warnOnce(!correct || bits <= 32, 'Cannot correct overflows of this many bits: ' + bits); - if (CHECK_OVERFLOWS) return 'CHECK_OVERFLOW(' + text + ', ' + bits + ', ' + Math.floor(correctSpecificOverflow() && !PGO) + ( - PGO ? ', "' + Debugging.getIdentifier() + '"' : '' - ) + ')'; + if (CHECK_OVERFLOWS) return 'CHECK_OVERFLOW(' + text + ', ' + bits + ', ' + Math.floor(correctSpecificOverflow()) + ')'; if (!correct) return text; if (bits == 32) { return '((' + text + ')|0)'; @@ -1827,9 +1817,7 @@ function makeSignOp(value, type, op, force, ignore) { var bits, full; if (type in Runtime.INT_TYPES) { bits = parseInt(type.substr(1)); - full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore || (correctSpecificSign() && !PGO)) + ( - PGO ? ', "' + (ignore ? '' : Debugging.getIdentifier()) + '"' : '' - ) + ')'; + full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore || (correctSpecificSign())) + ')'; // Always sign/unsign constants at compile time, regardless of CHECK/CORRECT if (isNumber(value)) { return eval(full).toString(); diff --git a/src/preamble.js b/src/preamble.js index aab50e9a..503b09f1 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -150,52 +150,6 @@ function SAFE_HEAP_COPY_HISTORY(dest, src) { //========================================== #endif -var CorrectionsMonitor = { -#if PGO - MAX_ALLOWED: Infinity, -#else - MAX_ALLOWED: 0, // XXX -#endif - corrections: 0, - sigs: {}, - - note: function(type, succeed, sig) { - if (!succeed) { - this.corrections++; - if (this.corrections >= this.MAX_ALLOWED) abort('\n\nToo many corrections!'); - } -#if PGO - if (!sig) - sig = (new Error().stack).toString().split('\n')[2].split(':').slice(-1)[0]; // Spidermonkey-specific FIXME - sig = type + '|' + sig; - if (!this.sigs[sig]) { - //Module.print('Correction: ' + sig); - this.sigs[sig] = [0, 0]; // fail, succeed - } - this.sigs[sig][succeed ? 1 : 0]++; -#endif - }, - - print: function() { -#if PGO - var items = []; - for (var sig in this.sigs) { - items.push({ - sig: sig, - fails: this.sigs[sig][0], - succeeds: this.sigs[sig][1], - total: this.sigs[sig][0] + this.sigs[sig][1] - }); - } - items.sort(function(x, y) { return y.total - x.total; }); - for (var i = 0; i < items.length; i++) { - var item = items[i]; - Module.print(item.sig + ' : ' + item.total + ' hits, %' + (Math.ceil(100*item.fails/item.total)) + ' failures'); - } -#endif - } -}; - #if CHECK_OVERFLOWS //======================================== // Debugging tools - Mathop overflows @@ -207,24 +161,20 @@ function CHECK_OVERFLOW(value, bits, ignore, sig) { // For signedness issue here, see settings.js, CHECK_SIGNED_OVERFLOWS #if CHECK_SIGNED_OVERFLOWS if (value === Infinity || value === -Infinity || value >= twopbits1 || value < -twopbits1) { - CorrectionsMonitor.note('SignedOverflow', 0, sig); - if (value === Infinity || value === -Infinity || Math.abs(value) >= twopbits) CorrectionsMonitor.note('Overflow'); + throw 'SignedOverflow'; + if (value === Infinity || value === -Infinity || Math.abs(value) >= twopbits) throw 'Overflow'; + } #else if (value === Infinity || value === -Infinity || Math.abs(value) >= twopbits) { - CorrectionsMonitor.note('Overflow', 0, sig); + throw 'Overflow'; + } #endif #if CORRECT_OVERFLOWS - // Fail on >32 bits - we warned at compile time - if (bits <= 32) { - value = value & (twopbits - 1); - } -#endif - } else { -#if CHECK_SIGNED_OVERFLOWS - CorrectionsMonitor.note('SignedOverflow', 1, sig); -#endif - CorrectionsMonitor.note('Overflow', 1, sig); + // Fail on >32 bits - we warned at compile time + if (bits <= 32) { + value = value & (twopbits - 1); } +#endif return value; } #endif @@ -243,39 +193,6 @@ var INDENT = ''; var START_TIME = Date.now(); #endif -#if PROFILE -var PROFILING = 0; -var PROFILING_ROOT = { time: 0, children: {}, calls: 0 }; -var PROFILING_NODE; - -function startProfiling() { - PROFILING_NODE = PROFILING_ROOT; - PROFILING = 1; -} -Module['startProfiling'] = startProfiling; - -function stopProfiling() { - PROFILING = 0; - assert(PROFILING_NODE === PROFILING_ROOT, 'Must have popped all the profiling call stack'); -} -Module['stopProfiling'] = stopProfiling; - -function printProfiling() { - function dumpData(name_, node, indent) { - Module.print(indent + ('________' + node.time).substr(-8) + ': ' + name_ + ' (' + node.calls + ')'); - var children = []; - for (var child in node.children) { - children.push(node.children[child]); - children[children.length-1].name_ = child; - } - children.sort(function(x, y) { return y.time - x.time }); - children.forEach(function(child) { dumpData(child.name_, child, indent + ' ') }); - } - dumpData('root', PROFILING_ROOT, ' '); -} -Module['printProfiling'] = printProfiling; -#endif - //======================================== // Runtime essentials //======================================== @@ -655,47 +572,43 @@ function enlargeMemory() { #endif var TOTAL_STACK = Module['TOTAL_STACK'] || {{{ TOTAL_STACK }}}; -#if ASM_JS == 0 var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || {{{ TOTAL_MEMORY }}}; -#else -var TOTAL_MEMORY = {{{ TOTAL_MEMORY }}}; // in asm, we hardcode the mask, so cannot adjust memory at runtime -#endif var FAST_MEMORY = Module['FAST_MEMORY'] || {{{ FAST_MEMORY }}}; // Initialize the runtime's memory #if USE_TYPED_ARRAYS // check for full engine support (use string 'subarray' to avoid closure compiler confusion) - assert(!!Int32Array && !!Float64Array && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']), - 'Cannot fallback to non-typed array case: Code is too specialized'); +assert(!!Int32Array && !!Float64Array && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']), + 'Cannot fallback to non-typed array case: Code is too specialized'); #if USE_TYPED_ARRAYS == 1 - HEAP = IHEAP = new Int32Array(TOTAL_MEMORY); - IHEAPU = new Uint32Array(IHEAP.buffer); +HEAP = IHEAP = new Int32Array(TOTAL_MEMORY); +IHEAPU = new Uint32Array(IHEAP.buffer); #if USE_FHEAP - FHEAP = new Float64Array(TOTAL_MEMORY); +FHEAP = new Float64Array(TOTAL_MEMORY); #endif #endif #if USE_TYPED_ARRAYS == 2 - var buffer = new ArrayBuffer(TOTAL_MEMORY); - HEAP8 = new Int8Array(buffer); - HEAP16 = new Int16Array(buffer); - HEAP32 = new Int32Array(buffer); - HEAPU8 = new Uint8Array(buffer); - HEAPU16 = new Uint16Array(buffer); - HEAPU32 = new Uint32Array(buffer); - HEAPF32 = new Float32Array(buffer); - HEAPF64 = new Float64Array(buffer); - - // Endianness check (note: assumes compiler arch was little-endian) - HEAP32[0] = 255; - assert(HEAPU8[0] === 255 && HEAPU8[3] === 0, 'Typed arrays 2 must be run on a little-endian system'); +var buffer = new ArrayBuffer(TOTAL_MEMORY); +HEAP8 = new Int8Array(buffer); +HEAP16 = new Int16Array(buffer); +HEAP32 = new Int32Array(buffer); +HEAPU8 = new Uint8Array(buffer); +HEAPU16 = new Uint16Array(buffer); +HEAPU32 = new Uint32Array(buffer); +HEAPF32 = new Float32Array(buffer); +HEAPF64 = new Float64Array(buffer); + +// Endianness check (note: assumes compiler arch was little-endian) +HEAP32[0] = 255; +assert(HEAPU8[0] === 255 && HEAPU8[3] === 0, 'Typed arrays 2 must be run on a little-endian system'); #endif #else - // Make sure that our HEAP is implemented as a flat array. - HEAP = []; // Hinting at the size with |new Array(TOTAL_MEMORY)| should help in theory but makes v8 much slower - for (var i = 0; i < FAST_MEMORY; i++) { - HEAP[i] = 0; // XXX We do *not* use {{| makeSetValue(0, 'i', 0, 'null') |}} here, since this is done just to optimize runtime speed - } +// Make sure that our HEAP is implemented as a flat array. +HEAP = []; // Hinting at the size with |new Array(TOTAL_MEMORY)| should help in theory but makes v8 much slower +for (var i = 0; i < FAST_MEMORY; i++) { + HEAP[i] = 0; // XXX We do *not* use {{| makeSetValue(0, 'i', 0, 'null') |}} here, since this is done just to optimize runtime speed +} #endif Module['HEAP'] = HEAP; @@ -722,7 +635,7 @@ STACK_MAX = TOTAL_STACK; // we lose a little stack here, but TOTAL_STACK is nice #if USE_TYPED_ARRAYS == 2 var tempDoublePtr = Runtime.alignMemory(allocate(12, 'i8', ALLOC_STACK), 8); assert(tempDoublePtr % 8 == 0); -function copyTempFloat(ptr) { // functions, because inlining this code is increases code size too much +function copyTempFloat(ptr) { // functions, because inlining this code increases code size too much HEAP8[tempDoublePtr] = HEAP8[ptr]; HEAP8[tempDoublePtr+1] = HEAP8[ptr+1]; HEAP8[tempDoublePtr+2] = HEAP8[ptr+2]; @@ -773,9 +686,6 @@ function preMain() { } function exitRuntime() { callRuntimeCallbacks(__ATEXIT__); - - // Print summary of correction activity - CorrectionsMonitor.print(); } // Tools diff --git a/src/runtime.js b/src/runtime.js index 95c74647..e902d27b 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -357,7 +357,7 @@ var Runtime = { }, addFunction: function(func, sig) { - assert(sig); + //assert(sig); // TODO: support asm var table = FUNCTION_TABLE; // TODO: support asm var ret = table.length; table.push(func); @@ -509,26 +509,19 @@ function getRuntime() { // example, -1 in int32 would be a very large number as unsigned. function unSign(value, bits, ignore, sig) { if (value >= 0) { -#if CHECK_SIGNS - if (!ignore) CorrectionsMonitor.note('UnSign', 1, sig); -#endif return value; } #if CHECK_SIGNS - if (!ignore) CorrectionsMonitor.note('UnSign', 0, sig); + if (!ignore) throw 'UnSign'; #endif return bits <= 32 ? 2*Math.abs(1 << (bits-1)) + value // Need some trickery, since if bits == 32, we are right at the limit of the bits JS uses in bitshifts : Math.pow(2, bits) + value; - // TODO: clean up previous line } // Converts a value we have as unsigned, into a signed value. For // example, 200 in a uint8 would be a negative number. function reSign(value, bits, ignore, sig) { if (value <= 0) { -#if CHECK_SIGNS - if (!ignore) CorrectionsMonitor.note('ReSign', 1, sig); -#endif return value; } var half = bits <= 32 ? Math.abs(1 << (bits-1)) // abs is needed if bits == 32 @@ -540,10 +533,7 @@ function reSign(value, bits, ignore, sig) { // but, in general there is no perfect solution here. With 64-bit ints, we get rounding and errors // TODO: In i64 mode 1, resign the two parts separately and safely #if CHECK_SIGNS - if (!ignore) { - CorrectionsMonitor.note('ReSign', 0, sig); - noted = true; - } + if (!ignore) throw 'ReSign'; #endif value = -2*half + value; // Cannot bitshift half, as it may be at the limit of the bits JS uses in bitshifts } @@ -552,18 +542,9 @@ function reSign(value, bits, ignore, sig) { // without CHECK_SIGNS, we would just do the |0 shortcut, so check that that // would indeed give the exact same result. if (bits === 32 && (value|0) !== value && typeof value !== 'boolean') { - if (!ignore) { - CorrectionsMonitor.note('ReSign', 0, sig); - noted = true; - } + if (!ignore) throw 'ReSign'; } - if (!noted) CorrectionsMonitor.note('ReSign', 1, sig); #endif return value; } -// Just a stub. We don't care about noting compile-time corrections. But they are called. -var CorrectionsMonitor = { - note: function(){} -}; - diff --git a/src/settings.js b/src/settings.js index 23898195..d036822f 100644 --- a/src/settings.js +++ b/src/settings.js @@ -134,6 +134,16 @@ var SAFE_HEAP_LOG = 0; // Log out all SAFE_HEAP operations var ASM_HEAP_LOG = 0; // Simple heap logging, like SAFE_HEAP_LOG but cheaper, and in asm.js +var CORRUPTION_CHECK = 0; // When enabled, will emit a buffer area at the beginning and + // end of each allocation on the heap, filled with canary + // values that can be checked later. Corruption is checked for + // at the end of each at each free() (see jsifier to add more, and you + // can add more manual checks by calling CorruptionChecker.checkAll). + // 0 means not enabled, higher values mean the size of the + // buffer areas as a multiple of the allocated area (so + // 1 means 100%, or buffer areas equal to allocated area, + // both before and after). This must be an integer. + var LABEL_DEBUG = 0; // 1: Print out functions as we enter them // 2: Also print out each label as we enter it var LABEL_FUNCTION_FILTERS = []; // Filters for function label debug. @@ -154,8 +164,6 @@ var GL_DEBUG = 0; // Print out all calls into WebGL. As with LIBRARY_DEBUG, you // option, in this case GL.debug. var GL_MAX_TEMP_BUFFER_SIZE = 2097152; // How large GL emulation temp buffers are -var PROFILE_MAIN_LOOP = 0; // Profile the function called in set_main_loop - var DISABLE_EXCEPTION_CATCHING = 0; // Disables generating code to actually catch exceptions. If the code you // are compiling does not actually rely on catching exceptions (but the // compiler generates code for it, maybe because of stdlibc++ stuff), @@ -205,21 +213,10 @@ var FS_LOG = 0; // Log all FS operations. This is especially helpful when you'r // a new project and want to see a list of file system operations happening // so that you can create a virtual file system with all of the required files. -var PGO = 0; // Profile-guided optimization. - // When run with the CHECK_* options, will not fail on errors. Instead, will - // keep a record of which checks succeeded and which failed. On shutdown, will - // print out that information. This is useful for knowing which lines need - // checking enabled and which do not, that is, this is a way to automate the - // generation of line data for CORRECT_*_LINES options. - // All CORRECT_* options default to 1 with PGO builds. - // See https://github.com/kripken/emscripten/wiki/Optimizing-Code for more info - var NAMED_GLOBALS = 0; // If 1, we use global variables for globals. Otherwise // they are referred to by a base plus an offset (called an indexed global), // saving global variables but adding runtime overhead. -var PROFILE = 0; // Enables runtime profiling. See test_profiling for a usage example. - var EXPORT_ALL = 0; // If true, we export all the symbols var EXPORTED_FUNCTIONS = ['_main']; // Functions that are explicitly exported. These functions are kept alive // through LLVM dead code elimination, and also made accessible outside of diff --git a/system/lib/libcxx/symbols b/system/lib/libcxx/symbols index 23d4a7a4..0d412de7 100644 --- a/system/lib/libcxx/symbols +++ b/system/lib/libcxx/symbols @@ -767,7 +767,6 @@ W _ZNKSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE3putES4_bRNS_8ios_baseEwe W _ZNKSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_bRNS_8ios_baseEwRKNS_12basic_stringIwS3_NS_9allocatorIwEEEE W _ZNKSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_bRNS_8ios_baseEwe - T _ZNKSt8bad_cast4whatEv T _ZNKSt9exception4whatEv T _ZNSt10bad_typeidC1Ev T _ZNSt10bad_typeidC2Ev @@ -2561,11 +2560,6 @@ d _ZNSt3__1L7__wcoutE d _ZNSt3__1L8__rs_mutE W _ZNSt3__1plIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_12basic_stringIT_T0_T1_EEPKS6_RKS9_ - T _ZNSt8bad_castC1Ev - T _ZNSt8bad_castC2Ev - T _ZNSt8bad_castD0Ev - T _ZNSt8bad_castD1Ev - T _ZNSt8bad_castD2Ev T _ZNSt9exceptionD0Ev T _ZNSt9exceptionD1Ev T _ZNSt9exceptionD2Ev @@ -2719,7 +2713,6 @@ D _ZTISt15underflow_error D _ZTISt16invalid_argument D _ZTISt16nested_exception - D _ZTISt8bad_cast D _ZTISt9exception C _ZTSNSt3__110__stdinbufIcEE C _ZTSNSt3__110__stdinbufIwEE @@ -2855,7 +2848,6 @@ D _ZTSSt15underflow_error D _ZTSSt16invalid_argument D _ZTSSt16nested_exception - D _ZTSSt8bad_cast D _ZTSSt9exception D _ZTTNSt3__110istrstreamE D _ZTTNSt3__110ostrstreamE @@ -2980,7 +2972,6 @@ D _ZTVSt15underflow_error D _ZTVSt16invalid_argument D _ZTVSt16nested_exception - D _ZTVSt8bad_cast D _ZTVSt9exception W _ZThn8_NKSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE3__XEv W _ZThn8_NKSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE3__cEv diff --git a/system/lib/libcxxabi/symbols b/system/lib/libcxxabi/symbols index 8ef205ba..b63f2f82 100644 --- a/system/lib/libcxxabi/symbols +++ b/system/lib/libcxxabi/symbols @@ -10,3 +10,10 @@ D _ZTVN10__cxxabiv121__vmi_class_type_infoE D _ZTVN10__cxxabiv123__fundamental_type_infoE D _ZTVN10__cxxabiv129__pointer_to_member_type_infoE + D _ZTSSt9type_info + T _ZNKSt8bad_cast4whatEv + T _ZNSt8bad_castC1Ev + T _ZNSt8bad_castC2Ev + D _ZTISt8bad_cast + D _ZTSSt8bad_cast + D _ZTVSt8bad_cast diff --git a/tests/csmith_driver.py b/tests/csmith_driver.py new file mode 100755 index 00000000..a244b73c --- /dev/null +++ b/tests/csmith_driver.py @@ -0,0 +1,50 @@ +#!/usr/bin/python + +''' +Runs csmith, a C fuzzer, and looks for bugs +''' + +import os, sys, difflib +from subprocess import Popen, PIPE, STDOUT + +sys.path += [os.path.join(os.path.dirname(os.path.dirname(__file__)), 'tools')] +import shared + +CSMITH = os.path.expanduser('~/Dev/csmith/src/csmith') +CSMITH_CFLAGS = ['-I' + os.path.expanduser('~/Dev/csmith/runtime/')] + +filename = os.path.join(shared.CANONICAL_TEMP_DIR, 'fuzzcode') + +shared.DEFAULT_TIMEOUT = 3 + +tried = 0 +valid = 0 + +while 1: + print 'Tried %d, valid: %d' % (tried, valid) + tried += 1 + print '1) Generate C' + shared.execute([CSMITH, '--no-volatiles', '--no-math64', '--max-block-depth', '2', '--max-block-size', '2', '--max-expr-complexity', '2', '--max-funcs', '1'], stdout=open(filename + '.c', 'w')) + + print '2) Compile natively' + shared.try_delete(filename) + shared.execute([shared.CLANG_CC, '-O2', filename + '.c', '-o', filename] + CSMITH_CFLAGS, stderr=PIPE) + assert os.path.exists(filename) + print '3) Run natively' + try: + correct = shared.timeout_run(Popen([filename], stdout=PIPE, stderr=PIPE), 3) + except Exception, e: + print 'Failed or infinite looping in native, skipping', e + continue + valid += 1 + + print '4) Compile JS-ly' + shared.try_delete(filename + '.js') + shared.execute([shared.EMCC, '-O2', filename + '.c', '-o', filename + '.js'] + CSMITH_CFLAGS, stderr=PIPE) + assert os.path.exists(filename + '.js') + print '5) Run JS-ly' + js = shared.run_js(filename + '.js', stderr=PIPE) #, engine=...) + + print '6) Verify' + assert correct == js, ''.join([a.rstrip()+'\n' for a in difflib.unified_diff(x.split('\n'), y.split('\n'), fromfile='expected', tofile='actual')]) + diff --git a/tests/cubegeom.c b/tests/cubegeom.c index c137ad80..6158b124 100644 --- a/tests/cubegeom.c +++ b/tests/cubegeom.c @@ -207,6 +207,20 @@ int main(int argc, char *argv[]) glNormalPointer(GL_BYTE, 32, (void*)12); glColorPointer(4, GL_UNSIGNED_BYTE, 32, (void*)28); + int temp; // test glGetPointerv, glGetIntegerv + glGetPointerv(GL_VERTEX_ARRAY_POINTER, &temp); assert(temp == 0); + glGetPointerv(GL_COLOR_ARRAY_POINTER, &temp); assert(temp == 28); + glGetPointerv(GL_TEXTURE_COORD_ARRAY_POINTER, &temp); assert(temp == 16); + glGetIntegerv(GL_VERTEX_ARRAY_SIZE, &temp); assert(temp == 3); + glGetIntegerv(GL_VERTEX_ARRAY_TYPE, &temp); assert(temp == GL_FLOAT); + glGetIntegerv(GL_VERTEX_ARRAY_STRIDE, &temp); assert(temp == 32); + glGetIntegerv(GL_COLOR_ARRAY_SIZE, &temp); assert(temp == 4); + glGetIntegerv(GL_COLOR_ARRAY_TYPE, &temp); assert(temp == GL_UNSIGNED_BYTE); + glGetIntegerv(GL_COLOR_ARRAY_STRIDE, &temp); assert(temp == 32); + glGetIntegerv(GL_TEXTURE_COORD_ARRAY_SIZE, &temp); assert(temp == 2); + glGetIntegerv(GL_TEXTURE_COORD_ARRAY_TYPE, &temp); assert(temp == GL_FLOAT); + glGetIntegerv(GL_TEXTURE_COORD_ARRAY_STRIDE, &temp); assert(temp == 32); + glBindTexture(GL_TEXTURE_2D, texture); // diffuse? glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE1); diff --git a/tests/cubegeom_pre_vao.c b/tests/cubegeom_pre_vao.c new file mode 100644 index 00000000..f1d35fb5 --- /dev/null +++ b/tests/cubegeom_pre_vao.c @@ -0,0 +1,333 @@ +/* +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> + +void verify() { + int width = 640, height = 480; + unsigned char *data = (unsigned char*)malloc(width*height*4); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); + int sum = 0; + for (int x = 0; x < width*height*4; x++) { + if (x % 4 != 3) sum += x * data[x]; + } +#if EMSCRIPTEN + int result = sum; + REPORT_RESULT(); +#endif +} + +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; + } + + glClearColor( 0, 0, 0, 0 ); + glClear( GL_COLOR_BUFFER_BIT ); + + // Create a texture + + GLuint texture; + glGenTextures( 1, &texture ); + glBindTexture( GL_TEXTURE_2D, texture ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + GLubyte textureData[16*16*4]; + for (int x = 0; x < 16; x++) { + for (int y = 0; y < 16; y++) { + *((int*)&textureData[(x*16 + y) * 4]) = x*16 + ((y*16) << 8); + } + } + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, + GL_RGBA, GL_UNSIGNED_BYTE, textureData ); + + // Create a second texture + + GLuint texture2; + glGenTextures( 1, &texture2 ); + glBindTexture( GL_TEXTURE_2D, texture2 ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + GLubyte texture2Data[] = { 0xff, 0, 0, 0xff, + 0, 0xff, 0, 0xaa, + 0, 0, 0xff, 0x55, + 0x80, 0x90, 0x70, 0 }; + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, + GL_RGBA, GL_UNSIGNED_BYTE, texture2Data ); + + // BEGIN + +#if USE_GLEW + glewInit(); +#endif + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + // original: glFrustum(-0.6435469817188064, 0.6435469817188064 ,-0.48266022190470925, 0.48266022190470925 ,0.5400000214576721, 2048); + //glFrustum(-0.6435469817188064, 0.1435469817188064 ,-0.48266022190470925, 0.88266022190470925 ,0.5400000214576721, 2048); + GLfloat pm[] = { 1.372136116027832, 0, 0, 0, 0, 0.7910231351852417, 0, 0, -0.6352481842041016, 0.29297152161598206, -1.0005275011062622, -1, 0, 0, -1.080284833908081, 0 }; + glLoadMatrixf(pm); + + glMatrixMode(GL_MODELVIEW); + GLfloat matrixData[] = { -1, 0, 0, 0, + 0, 0,-1, 0, + 0, 1, 0, 0, + 0, 0, 0, 1 }; + glLoadMatrixf(matrixData); + + glActiveTexture(GL_TEXTURE0); + + GLuint arrayBuffer, elementBuffer; + glGenBuffers(1, &arrayBuffer); + glGenBuffers(1, &elementBuffer); + + GLubyte arrayData[] = { +/* +[0, 0, 0, 67] ==> 128 float +[0, 0, 128, 67] ==> 256 float +[0, 0, 0, 68] ==> 512 float +[0, 0, 128, 68] ==> 1024 float + +[vertex x ] [vertex y ] [vertex z ] [nr] [texture u ] [texture v ] [lm u ] [lm v ] [color r,g,b,a ] */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, // 0 + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, // 1 + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 2 + 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 3 + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, // 4 + 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 128, 67, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, // 5 + 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 128, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 6 + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 7 + 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 8 + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 9 + 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 128, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 10 + 0, 0, 0, 0, 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 11 + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 12 + 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 128, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 13 + 0, 0, 128, 68, 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 128, 67, 0, 0, 128, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 14 + 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 128, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 15 + + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 128, 68, 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128 + }; + + // Generate a VAO + GLuint vao; + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + assert(sizeof(arrayData) == 1408); + glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(arrayData), arrayData, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + GLushort elementData[] = { 1, 2, 0, 2, 3, 0, 5, 6, 4, 6, 7, 4, 9, 10, 8, 10, 11, 8, 13, 14, 12, 14, 15, 12 }; + assert(sizeof(elementData) == 48); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elementData), elementData, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer); + + glBindTexture(GL_TEXTURE_2D, texture); // diffuse? + glActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, texture2); // lightmap? + glActiveTexture(GL_TEXTURE0); + + GLint ok; + + const char *vertexShader = "attribute vec4 a_position;\n" + "attribute vec4 a_texCoord0;\n" + "uniform mat4 u_modelView;\n" + "uniform mat4 u_projection;\n" + "varying vec4 v_texCoord0;\n" + "void main(void)\n" + "{\n" + " gl_Position = (u_projection * u_modelView * a_position) + vec4(200, 0, 0, 0);\n" + " v_texCoord0.xy = a_texCoord0.xy/20.0;\n" // added /20 here + "}\n"; + const char *fragmentShader = "uniform sampler2D diffusemap;\n" + "varying vec4 v_texCoord0;\n" + "void main(void)\n" + "{\n" + " vec4 diffuse = texture2D(diffusemap, v_texCoord0.xy);\n" + " gl_FragColor = diffuse;\n" + "}\n"; + + GLuint vs = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vs, 1, &vertexShader, NULL); + glCompileShader(vs); + glGetShaderiv(vs, GL_COMPILE_STATUS, &ok); + if (!ok) { + printf("Shader compilation error with vertex\n"); + GLint infoLen = 0; + glGetShaderiv (vs, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen > 1) + { + char* infoLog = (char *)malloc(sizeof(char) * infoLen+1); + glGetShaderInfoLog(vs, infoLen, NULL, infoLog); + printf("Error compiling shader:\n%s\n", infoLog); + } + } + + GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fs, 1, &fragmentShader, NULL); + glCompileShader(fs); + glGetShaderiv(fs, GL_COMPILE_STATUS, &ok); + if (!ok) { + printf("Shader compilation error with fragment\n"); + GLint infoLen = 0; + glGetShaderiv (vs, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen > 1) + { + char* infoLog = (char *)malloc(sizeof(char) * infoLen+1); + glGetShaderInfoLog(vs, infoLen, NULL, infoLog); + printf("Error compiling shader:\n%s\n", infoLog); + } + } + + GLuint program = glCreateProgram(); + + glAttachShader(program, vs); + glAttachShader(program, fs); + glLinkProgram(program); + glGetProgramiv(program, GL_LINK_STATUS, &ok); + assert(ok); + + glUseProgram(program); + + GLint diffusemapLocation = glGetUniformLocation(program, "diffusemap"); + assert(diffusemapLocation >= 0); + glUniform1i(diffusemapLocation, 0); + + { + GLfloat data[16]; + glGetFloatv(GL_MODELVIEW_MATRIX, data); + printf("Modelview: "); + for (int i = 0; i < 16; i++) printf("%.3f, ", data[i]); + printf("\n"); + GLint modelViewLocation = glGetUniformLocation(program, "u_modelView"); + assert(modelViewLocation >= 0); + glUniformMatrix4fv(modelViewLocation, 1, GL_FALSE, data); + } + { + GLfloat data[16]; + glGetFloatv(GL_PROJECTION_MATRIX, data); + printf("Projection: "); + for (int i = 0; i < 16; i++) printf("%.3f, ", data[i]); + printf("\n"); + GLint projectionLocation = glGetUniformLocation(program, "u_projection"); + assert(projectionLocation >= 0); + glUniformMatrix4fv(projectionLocation, 1, GL_FALSE, data); + } + + glBindAttribLocation(program, 0, "a_position"); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 32, (void*)0); + glEnableVertexAttribArray(0); + + glBindAttribLocation(program, 1, "a_texCoord0"); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 32, (void*)16); + glEnableVertexAttribArray(1); + + // stop recording in the VAO + + glBindVertexArray(0); + + // unbind all the stuff the VAO would save for us, so this is a valid test + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + + // draw with VAO + + glBindVertexArray(vao); + + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void*)12); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void*) 0); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void*)24); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void*)36); + + glBindVertexArray(0); + + glDeleteVertexArrays(1, &vao); + + // END + + SDL_GL_SwapBuffers(); + + verify(); + +#if !EMSCRIPTEN + SDL_Delay(1500); +#endif + + SDL_Quit(); + + return 0; +} diff --git a/tests/gl_ps_workaround2.c b/tests/gl_ps_workaround2.c new file mode 100644 index 00000000..e5bd2fd1 --- /dev/null +++ b/tests/gl_ps_workaround2.c @@ -0,0 +1,230 @@ +/******************************************************************* + * * + * Using SDL With OpenGL * + * * + * Tutorial by Kyle Foley (sdw) * + * * + * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL * + * * + *******************************************************************/ + +/* +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" +#include "SDL/SDL_image.h" +#if !USE_GLEW +#include "SDL/SDL_opengl.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +void shaders() { +#if USE_GLEW + glewInit(); +#endif + + GLint ok; + + const char *vertexShader = "void main(void) \n" + "{ \n" + " gl_Position = ftransform(); \n" + " gl_TexCoord[0] = gl_MultiTexCoord0; \n" + " gl_FrontColor = gl_Color; \n" + "} \n"; + const char *fragmentShader = "uniform sampler2D tex0; \n" + "void main(void) \n" + "{ \n" + " gl_FragColor = gl_Color * texture2D(tex0, gl_TexCoord[0].xy); \n" + "} \n"; + + GLuint vs = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vs, 1, &vertexShader, NULL); + glCompileShader(vs); + glGetShaderiv(vs, GL_COMPILE_STATUS, &ok); + assert(ok); + + GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fs, 1, &fragmentShader, NULL); + glCompileShader(fs); + glGetShaderiv(fs, GL_COMPILE_STATUS, &ok); + assert(ok); + + GLuint program = glCreateProgram(); + + glAttachShader(program, vs); + glAttachShader(program, fs); + glLinkProgram(program); + glGetProgramiv(program, GL_LINK_STATUS, &ok); + assert(ok); + + glUseProgram(program); +} + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + + // Slightly different SDL initialization + 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 ); // *new* + + screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed* + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Set the OpenGL state after creating the context with SDL_SetVideoMode + + glClearColor( 0, 0, 0, 0 ); + +#if !EMSCRIPTEN + glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL +#endif + + glViewport( 0, 0, 640, 480 ); + + glMatrixMode( GL_PROJECTION ); + GLfloat matrixData[] = { 2.0/640, 0, 0, 0, + 0, -2.0/480, 0, 0, + 0, 0, -1, 0, + -1, 1, 0, 1 }; + glLoadMatrixf(matrixData); // test loadmatrix + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Load the OpenGL texture + + GLuint texture; // Texture object handle + SDL_Surface *surface; // Gives us the information to make the texture + + if ( (surface = IMG_Load("screenshot.png")) ) { + + // Check that the image's width is a power of 2 + if ( (surface->w & (surface->w - 1)) != 0 ) { + printf("warning: image.bmp's width is not a power of 2\n"); + } + + // Also check if the height is a power of 2 + if ( (surface->h & (surface->h - 1)) != 0 ) { + printf("warning: image.bmp's height is not a power of 2\n"); + } + + // Have OpenGL generate a texture object handle for us + glGenTextures( 1, &texture ); + + // Bind the texture object + glBindTexture( GL_TEXTURE_2D, texture ); + + // Set the texture's stretching properties + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + //SDL_LockSurface(surface); + + // Add some greyness + memset(surface->pixels, 0x66, surface->w*surface->h); + + // Edit the texture object's image data using the information SDL_Surface gives us + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, surface->w, surface->h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels ); + + //SDL_UnlockSurface(surface); + } + else { + printf("SDL could not load image.bmp: %s\n", SDL_GetError()); + SDL_Quit(); + return 1; + } + + // Free the SDL_Surface only if it was successfully created + if ( surface ) { + SDL_FreeSurface( surface ); + } + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + shaders(); + + // Bind the texture to which subsequent calls refer to + glBindTexture( GL_TEXTURE_2D, texture ); + + // Use clientside vertex pointers to render two items. In this test we have each + // attribute in a separate buffer, packed (i.e. stride == 0) + GLfloat vertexData[] = { 10, 10, + 300, 10, + 300, 128, + 10, 128, + 410, 10, + 600, 10, + 630, 200, + 310, 250, + 100, 300, + 300, 300, + 300, 400, + 100, 400 }; + GLfloat textureData[] = { 0, 0, + 1, 0, + 1, 1, + 0, 1, + 0, 0.5, + 1, 0.5, + 1, 1, + 0.5, 1, + 0, 0, + 1, 0, + 1, 1, + 0, 1, }; + + glEnable(GL_TEXTURE_2D); // XXX should be GL_TEXTURE_COORD_ARRAY); and also glEnableClientState! XXX two workarounds here + glTexCoordPointer(2, GL_FLOAT, 0, textureData); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, vertexData); + + glDrawArrays(GL_QUADS, 0, 12); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + SDL_GL_SwapBuffers(); + +#if !EMSCRIPTEN + // Wait for 3 seconds to give us a chance to see the image + SDL_Delay(3000); +#endif + + // Now we can delete the OpenGL texture and close down SDL + glDeleteTextures( 1, &texture ); + + SDL_Quit(); + + return 0; +} diff --git a/tests/runner.py b/tests/runner.py index ee710fc9..39869630 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -100,14 +100,8 @@ class RunnerCore(unittest.TestCase): for temp_file in os.listdir(TEMP_DIR): if temp_file.endswith('.ll'): self.has_prev_ll = True - + def tearDown(self): - if self.save_JS: - for name in os.listdir(self.get_dir()): - if name.endswith(('.o.js', '.cc.js')): - suff = '.'.join(name.split('.')[-2:]) - shutil.copy(os.path.join(self.get_dir(), name), - os.path.join(TEMP_DIR, self.id().replace('__main__.', '').replace('.test_', '.')+'.'+suff)) if not self.save_dir: # rmtree() fails on Windows if the current working directory is inside the tree. os.chdir(os.path.join(self.get_dir(), '..')) @@ -140,6 +134,12 @@ class RunnerCore(unittest.TestCase): def get_stdout_path(self): return os.path.join(self.get_dir(), 'stdout') + def hardcode_arguments(self, filename, args): + # Hardcode in the arguments, so js is portable without manual commandlinearguments + if not args: return + js = open(filename).read() + open(filename, 'w').write(js.replace('var ret = run();', 'var ret = run(%s);' % str(args))) + def prep_ll_run(self, filename, ll_file, force_recompile=False, build_ll_hook=None): if ll_file.endswith(('.bc', '.o')): if ll_file != filename + '.o': @@ -435,6 +435,8 @@ process(sys.argv[1]) sys.argv = map(lambda arg: arg if not arg.startswith('test_') else 'default.' + arg, sys.argv) +test_index = 0 + if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv) and 'browser' not in str(sys.argv): # Tests @@ -448,29 +450,35 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv) and 'brows class T(RunnerCore): # Short name, to make it more fun to use manually on the commandline ## Does a complete test - builds, runs, checks output, etc. def do_run(self, src, expected_output, args=[], output_nicerizer=None, output_processor=None, no_build=False, main_file=None, additional_files=[], js_engines=None, post_build=None, basename='src.cpp', libraries=[], includes=[], force_c=False, build_ll_hook=None, extra_emscripten_args=[]): - if force_c or (main_file is not None and main_file[-2:]) == '.c': - basename = 'src.c' - Building.COMPILER = to_cc(Building.COMPILER) - - dirname = self.get_dir() - filename = os.path.join(dirname, basename) - if not no_build: - self.build(src, dirname, filename, main_file=main_file, additional_files=additional_files, libraries=libraries, includes=includes, - build_ll_hook=build_ll_hook, extra_emscripten_args=extra_emscripten_args, post_build=post_build) - - # Run in both JavaScript engines, if optimizing - significant differences there (typed arrays) - if js_engines is None: - js_engines = JS_ENGINES - if Settings.USE_TYPED_ARRAYS: - js_engines = filter(lambda engine: engine != V8_ENGINE, js_engines) # V8 issue 1822 - js_engines = filter(lambda engine: engine not in self.banned_js_engines, js_engines) - if len(js_engines) == 0: return self.skip('No JS engine present to run this test with. Check %s and the paths therein.' % EM_CONFIG) - for engine in js_engines: - js_output = self.run_generated_code(engine, filename + '.o.js', args, output_nicerizer=output_nicerizer) - self.assertContained(expected_output, js_output.replace('\r\n', '\n')) - self.assertNotContained('ERROR', js_output) - - #shutil.rmtree(dirname) # TODO: leave no trace in memory. But for now nice for debugging + if force_c or (main_file is not None and main_file[-2:]) == '.c': + basename = 'src.c' + Building.COMPILER = to_cc(Building.COMPILER) + + dirname = self.get_dir() + filename = os.path.join(dirname, basename) + if not no_build: + self.build(src, dirname, filename, main_file=main_file, additional_files=additional_files, libraries=libraries, includes=includes, + build_ll_hook=build_ll_hook, extra_emscripten_args=extra_emscripten_args, post_build=post_build) + + # Run in both JavaScript engines, if optimizing - significant differences there (typed arrays) + if js_engines is None: + js_engines = JS_ENGINES + if Settings.USE_TYPED_ARRAYS: + js_engines = filter(lambda engine: engine != V8_ENGINE, js_engines) # V8 issue 1822 + js_engines = filter(lambda engine: engine not in self.banned_js_engines, js_engines) + if len(js_engines) == 0: return self.skip('No JS engine present to run this test with. Check %s and the paths therein.' % EM_CONFIG) + for engine in js_engines: + js_output = self.run_generated_code(engine, filename + '.o.js', args, output_nicerizer=output_nicerizer) + self.assertContained(expected_output, js_output.replace('\r\n', '\n')) + self.assertNotContained('ERROR', js_output) + + #shutil.rmtree(dirname) # TODO: leave no trace in memory. But for now nice for debugging + + if self.save_JS: + global test_index + self.hardcode_arguments(filename + '.o.js', args) + shutil.copyfile(filename + '.o.js', os.path.join(TEMP_DIR, str(test_index) + '.js')) + test_index += 1 # No building - just process an existing .ll file (or .bc, which we turn into .ll) def do_ll_run(self, ll_file, expected_output=None, args=[], js_engines=None, output_nicerizer=None, post_build=None, force_recompile=False, build_ll_hook=None, extra_emscripten_args=[]): @@ -2845,6 +2853,23 @@ Exiting setjmp function, level: 0, prev_jmp: -1 ''' self.do_run(src, 'a1: 0\na2: 0\na3: 1\nb1: 0\nb2: 1\nb3: 1\nc1: 1\nc2: 1\nc3: 1\n') + def test_dynamic_cast_2(self): + if self.emcc_args is None: return self.skip('need libcxxabi') + + src = r''' + #include <stdio.h> + #include <typeinfo> + + class Class {}; + + int main() { + const Class* dp = dynamic_cast<const Class*>(&typeid(Class)); + // should return dp == NULL, + printf("pointer: %p\n", dp); + } + ''' + self.do_run(src, "pointer: (nil)") + def test_funcptr(self): src = ''' #include <stdio.h> @@ -5324,9 +5349,30 @@ Pass: 0.000012 0.000012''') return(0); } ''' - self.do_run(src, '3\nday 19, month Nov, year 2012'); + def test_sscanf_5(self): + src = r''' + #include "stdio.h" + + static const char *colors[] = { + " c black", + ". c #001100", + "X c #111100" + }; + + int main(){ + unsigned char code; + char color[32]; + int rcode; + for(int i = 0; i < 3; i++) { + rcode = sscanf(colors[i], "%c c %s", &code, color); + printf("%i, %c, %s\n", rcode, code, color); + } + } + ''' + self.do_run(src, '2, , black\n2, ., #001100\n2, X, #111100'); + def test_langinfo(self): src = open(path_from_root('tests', 'langinfo', 'test.c'), 'r').read() expected = open(path_from_root('tests', 'langinfo', 'output.txt'), 'r').read() @@ -6855,7 +6901,7 @@ def process(filename): if self.emcc_args is None: return self.skip('Very slow without ta2, and we would also need to include dlmalloc manually without emcc') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO FIXME') - Settings.CORRECT_SIGNS = 1 # XXX: in default, we fail with 2 here, even though the pgo_data should be correct (and works in s_0_0). Investigate this. + Settings.CORRECT_SIGNS = 1 Settings.CORRECT_OVERFLOWS = 0 Settings.CORRECT_ROUNDINGS = 0 if self.emcc_args is None: Settings.SAFE_HEAP = 0 # uses time.h to set random bytes, other stuff @@ -7185,40 +7231,101 @@ def process(filename): ''' self.do_run(src, '''AD:-1,1''', build_ll_hook=self.do_autodebug) - def test_profiling(self): - if Settings.ASM_JS: return self.skip('asm does not support profiling') + def test_corruption(self): + if Settings.ASM_JS: return self.skip('cannot use corruption checks in asm') + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2 for actual test') - src = ''' - #include <emscripten.h> - #include <unistd.h> + Settings.CORRUPTION_CHECK = 1 - int main() + src = r''' + #include <stdio.h> + #include <stdlib.h> + #include <string.h> + int main(int argc, char **argv) { + int size = 1024*argc; + char *buffer = (char*)malloc(size); + #if CORRUPT + memset(buffer, argc, size+15); + #else + memset(buffer, argc, size); + #endif + for (int x = 0; x < size; x += argc*3) buffer[x] = x/3; + int ret = 0; + for (int x = 0; x < size; x++) ret += buffer[x]; + free(buffer); + printf("All ok, %d\n", ret); + } + ''' + + for corrupt in [1]: + self.do_run(src.replace('CORRUPT', str(corrupt)), 'Heap corruption detected!' if corrupt else 'All ok, 4209') + + def test_corruption_2(self): + if Settings.ASM_JS: return self.skip('cannot use corruption checks in asm') + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2 for actual test') + + Settings.SAFE_HEAP = 1 + Settings.CORRUPTION_CHECK = 1 + + # test for free(0), malloc(0), etc. + src = r''' + #include <iostream> + #include <fstream> + #include <stdlib.h> + #include <stdio.h> + + void bye() { + printf("all ok\n"); + } + + int main() { + atexit(bye); + + std::string testPath = "/Script/WA-KA.txt"; + std::fstream str(testPath.c_str(), std::ios::in | std::ios::binary); + + if (str.is_open()) { - EMSCRIPTEN_PROFILE_INIT(3); - EMSCRIPTEN_PROFILE_BEGIN(0); - usleep(10 * 1000); - EMSCRIPTEN_PROFILE_END(0); - EMSCRIPTEN_PROFILE_BEGIN(1); - usleep(50 * 1000); - EMSCRIPTEN_PROFILE_END(1); - EMSCRIPTEN_PROFILE_BEGIN(2); - usleep(250 * 1000); - EMSCRIPTEN_PROFILE_END(2); - return 0; + std::cout << "open!" << std::endl; + } else { + std::cout << "missing!" << std::endl; } + + return 1; + } ''' + self.do_run(src, 'missing!\nall ok\n') - post1 = ''' -def process(filename): - src = open(filename, 'a') - src.write(\'\'\' - Profiling.dump(); - \'\'\') - src.close() -''' + def test_corruption_3(self): + if Settings.ASM_JS: return self.skip('cannot use corruption checks in asm') + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2 for actual test') - self.do_run(src, '''Profiling data: -Block 0: ''', post_build=post1) + Settings.CORRUPTION_CHECK = 1 + + # realloc + src = r''' + #include <stdlib.h> + #include <stdio.h> + #include <assert.h> + + void bye() { + printf("all ok\n"); + } + + int main(int argc, char **argv) { + atexit(bye); + + char *buffer = (char*)malloc(100); + for (int i = 0; i < 100; i++) buffer[i] = (i*i)%256; + buffer = (char*)realloc(buffer, argc + 50); + for (int i = 0; i < argc + 50; i++) { + //printf("%d : %d : %d : %d\n", i, (int)(buffer + i), buffer[i], (char)((i*i)%256)); + assert(buffer[i] == (char)((i*i)%256)); + } + return 1; + } + ''' + self.do_run(src, 'all ok\n') ### Integration tests @@ -7748,6 +7855,8 @@ 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') src = ''' #include <stdio.h> @@ -8123,7 +8232,6 @@ class %s(T): Settings.ASSERTIONS = 1-embetter Settings.SAFE_HEAP = 1-(embetter and llvm_opts) Building.LLVM_OPTS = llvm_opts - Settings.PGO = 0 Settings.CHECK_OVERFLOWS = 1-(embetter or llvm_opts) Settings.CORRECT_OVERFLOWS = 1-(embetter and llvm_opts) Settings.CORRECT_SIGNS = 0 @@ -8133,7 +8241,6 @@ class %s(T): Settings.INIT_STACK = 0 Settings.RUNTIME_TYPE_INFO = 0 Settings.DISABLE_EXCEPTION_CATCHING = 0 - Settings.PROFILE = 0 Settings.INCLUDE_FULL_LIBRARY = 0 Settings.BUILD_AS_SHARED_LIB = 0 Settings.RUNTIME_LINKED_LIBS = [] @@ -8156,11 +8263,11 @@ TT = %s exec('o1 = make_run("o1", compiler=CLANG, emcc_args=["-O1", "-s", "SAFE_HEAP=1"])') # Make one run with -O2, but without closure (we enable closure in specific tests, otherwise on everything it is too slow) - exec('o2 = make_run("o2", compiler=CLANG, emcc_args=["-O2", "--closure", "0"])') + exec('o2 = make_run("o2", compiler=CLANG, emcc_args=["-O2"])') # asm.js - #exec('asm = make_run("asm", compiler=CLANG, emcc_args=["-O0", "--closure", "0", "-s", "ASM_JS=1"])') - exec('asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2", "--closure", "0", "-s", "ASM_JS=1"])') + #exec('asm = make_run("asm", compiler=CLANG, emcc_args=["-O0", "-s", "ASM_JS=1"])') + exec('asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2", "-s", "ASM_JS=1"])') # Make custom runs with various options for compiler, quantum, embetter, typed_arrays, llvm_opts in [ @@ -8186,11 +8293,12 @@ TT = %s # --version output = Popen([PYTHON, compiler, '--version'], stdout=PIPE, stderr=PIPE).communicate() - self.assertContained('''emcc (Emscripten GCC-like replacement) 2.0 -Copyright (C) 2012 the Emscripten authors. + output = output[0].replace('\r', '') + self.assertContained('''emcc (Emscripten GCC-like replacement)''', output) + self.assertContained('''Copyright (C) 2013 the Emscripten authors (see AUTHORS.txt) This is free and open source software under the MIT license. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -''', output[0].replace('\r', ''), output[1].replace('\r', '')) +''', output) # -v, without input files output = Popen([PYTHON, compiler, '-v'], stdout=PIPE, stderr=PIPE).communicate() @@ -8296,20 +8404,22 @@ Options that are modified or new in %s include: (['-o', 'something.js', '-O0'], 0, None, 0, 0), (['-o', 'something.js', '-O1'], 1, None, 0, 0), (['-o', 'something.js', '-O1', '--closure', '1'], 1, None, 1, 0), - (['-o', 'something.js', '-O2'], 2, None, 1, 1), + (['-o', 'something.js', '-O2'], 2, None, 0, 1), (['-o', 'something.js', '-O2', '--closure', '0'], 2, None, 0, 0), + (['-o', 'something.js', '-O2', '-g'], 2, None, 0, 0), (['-o', 'something.js', '-O3'], 3, None, 1, 1), (['-o', 'something.js', '-O3', '--closure', '0'], 3, None, 0, 0), # and, test compiling to bitcode first (['-o', 'something.bc'], 0, [], 0, 0), (['-o', 'something.bc'], 0, ['-O0'], 0, 0), (['-o', 'something.bc'], 1, ['-O1'], 0, 0), - (['-o', 'something.bc'], 2, ['-O2'], 1, 0), + (['-o', 'something.bc'], 2, ['-O2'], 0, 0), (['-o', 'something.bc'], 3, ['-O3'], 1, 0), (['-O1', '-o', 'something.bc'], 0, [], 0, 0), # -Ox is ignored and warned about ]: - #print params, opt_level, bc_params, closure + print params, opt_level, bc_params, closure, has_malloc self.clear() + keep_debug = '-g' in params output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world_loop' + ('_malloc' if has_malloc else '') + '.cpp')] + params, stdout=PIPE, stderr=PIPE).communicate() assert len(output[0]) == 0, output[0] @@ -8333,14 +8443,13 @@ Options that are modified or new in %s include: else: # closure has not been run, we can do some additional checks. TODO: figure out how to do these even with closure assert 'Module._main = ' not in generated, 'closure compiler should not have been run' - # XXX find a way to test this: assert ('& 255' in generated or '&255' in generated) == (opt_level <= 2), 'corrections should be in opt <= 2' - assert ('(label)' in generated) == (opt_level <= 1), 'relooping should be in opt >= 2' - assert ('assert(STACKTOP < STACK_MAX' in generated) == (opt_level == 0), 'assertions should be in opt == 0' - assert 'var $i;' in generated or 'var $i_0' in generated or 'var $storemerge3;' in generated or 'var $storemerge4;' in generated or 'var $i_04;' in generated, 'micro opts should always be on' + if keep_debug: + assert ('(label)' in generated) == (opt_level <= 1), 'relooping should be in opt >= 2' + assert ('assert(STACKTOP < STACK_MAX' in generated) == (opt_level == 0), 'assertions should be in opt == 0' + assert 'var $i;' in generated or 'var $i_0' in generated or 'var $storemerge3;' in generated or 'var $storemerge4;' in generated or 'var $i_04;' in generated, 'micro opts should always be on' if opt_level >= 2: - assert re.search('HEAP8\[\$\w+ \+ \(+\$\w+ ', generated) or re.search('HEAP8\[HEAP32\[', generated), 'eliminator should create compound expressions, and fewer one-time vars' # also in -O1, but easier to test in -O2 + assert re.search('HEAP8\[\$?\w+ \+ \(+\$?\w+ ', generated) or re.search('HEAP8\[HEAP32\[', generated), 'eliminator should create compound expressions, and fewer one-time vars' # also in -O1, but easier to test in -O2 assert ('_puts(' in generated) == (opt_level >= 1), 'with opt >= 1, llvm opts are run and they should optimize printf to puts' - assert ('function _malloc(bytes) {' in generated) == (not has_malloc), 'If malloc is needed, it should be there, if not not' assert 'function _main() {' in generated, 'Should be unminified, including whitespace' assert ('-O3' in (params+(bc_params or []))) or'function _dump' in generated, 'No inlining by default' @@ -9083,7 +9192,7 @@ f.close() return 0; } ''') - Popen([PYTHON, EMCC, '-O2', '--closure', '-0', os.path.join(self.get_dir(), 'main.cpp')]).communicate() + Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'main.cpp')]).communicate() output = run_js(os.path.join(self.get_dir(), 'a.out.js'), full_output=True, stderr=PIPE) self.assertContained('''0:0 1:1 @@ -9295,6 +9404,26 @@ f.close() finally: del os.environ['EMCC_DEBUG'] + def test_debuginfo(self): + if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode') + try: + os.environ['EMCC_DEBUG'] = '1' + # llvm debug info is kept only when we can see it, which is without the js optimize, -O0. js debug info is lost by registerize in -O2, so - g disables it + for args, expect_llvm, expect_js in [ + (['-O0'], True, True), + (['-O0', '-g'], True, True), + (['-O1'], False, True), + (['-O1', '-g'], False, True), + (['-O2'], False, False), + (['-O2', '-g'], False, True), + ]: + print args, expect_llvm, expect_js + output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp')] + args, stdout=PIPE, stderr=PIPE).communicate() + assert expect_llvm == ('strip-debug' not in err) + assert expect_js == ('registerize' not in err) + finally: + del os.environ['EMCC_DEBUG'] + def test_scons(self): # also incidentally tests c++11 integration in llvm 3.1 try_delete(os.path.join(self.get_dir(), 'test')) shutil.copytree(path_from_root('tests', 'scons'), os.path.join(self.get_dir(), 'test')) @@ -10453,10 +10582,13 @@ elif 'browser' in str(sys.argv): self.btest('gl_ps_packed.c', reference='gl_ps.png', args=['--preload-file', 'screenshot.png']) def test_gl_ps_workaround(self): - # packed data that needs to be strided shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.btest('gl_ps_workaround.c', reference='gl_ps.png', args=['--preload-file', 'screenshot.png']) + def test_gl_ps_workaround2(self): + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.btest('gl_ps_workaround2.c', reference='gl_ps.png', args=['--preload-file', 'screenshot.png']) + def test_matrix_identity(self): self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840']) @@ -10505,6 +10637,9 @@ elif 'browser' in str(sys.argv): def test_cubegeom_fog(self): self.btest('cubegeom_fog.c', expected=['1617140399', '-898782526', '-946179526']) + def test_cubegeom_pre_vao(self): + self.btest('cubegeom_pre_vao.c', expected=['-1472804742', '-1626058463', '-2046234971']) + def test_cube_explosion(self): self.btest('cube_explosion.c', expected=['667220544', '-1543354600', '-1485258415']) @@ -10594,10 +10729,10 @@ elif 'browser' in str(sys.argv): main, supp = self.setup_runtimelink_test() open(self.in_dir('supp.cpp'), 'w').write(supp) - Popen([PYTHON, EMCC, self.in_dir('supp.cpp'), '-o', 'supp.js', '-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-s', 'BUILD_AS_SHARED_LIB=2', '-O2', '--closure', '0']).communicate() + Popen([PYTHON, EMCC, self.in_dir('supp.cpp'), '-o', 'supp.js', '-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-s', 'BUILD_AS_SHARED_LIB=2', '-O2']).communicate() shutil.move(self.in_dir('supp.js'), self.in_dir('supp.so')) - self.btest(main, args=['-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-s', 'RUNTIME_LINKED_LIBS=["supp.so"]', '-DBROWSER=1', '-O2', '--closure', '0'], expected='76') + self.btest(main, args=['-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-s', 'RUNTIME_LINKED_LIBS=["supp.so"]', '-DBROWSER=1', '-O2'], expected='76') def test_pre_run_deps(self): # Adding a dependency in preRun will delay run @@ -10899,6 +11034,9 @@ elif 'benchmark' in str(sys.argv): '-o', final_filename] + shared_args + emcc_args, stdout=PIPE, stderr=self.stderr_redirect).communicate() assert os.path.exists(final_filename), 'Failed to compile file: ' + output[0] + if self.save_JS: + self.hardcode_arguments(final_filename, args) + # Run JS global total_times, tests_done times = [] @@ -11151,7 +11289,7 @@ elif 'benchmark' in str(sys.argv): def test_dlmalloc(self): # XXX This seems to have regressed slightly with emcc. Are -g and the signs lines passed properly? src = open(path_from_root('system', 'lib', 'dlmalloc.c'), 'r').read() + '\n\n\n' + open(path_from_root('tests', 'dlmalloc_test.c'), 'r').read() - self.do_benchmark('dlmalloc', src, ['400', '3000'], '*3000,0*', emcc_args=['-g', '-s', 'CORRECT_SIGNS=2', '-s', 'CORRECT_SIGNS_LINES=[4820, 4195, 4250, 4203, 4209, 4239, 4231]']) + self.do_benchmark('dlmalloc', src, ['400', '3000'], '*3000,0*') def test_zlib(self): src = open(path_from_root('tests', 'zlib', 'benchmark.c'), 'r').read() @@ -11311,12 +11449,12 @@ elif 'sanity' in str(sys.argv): f = open(CONFIG_FILE, 'a') f.write('CLOSURE_COMPILER = "/tmp/nowhere/nothingtoseehere/kjadsfkjwelkjsdfkqgas/nonexistent.txt"\n') f.close() - output = self.check_working([EMCC, '-O2', 'tests/hello_world.cpp'], CLOSURE_FATAL) + output = self.check_working([EMCC, '-O2', '--closure', '1', 'tests/hello_world.cpp'], CLOSURE_FATAL) # With a working path, all is well restore() try_delete('a.out.js') - output = self.check_working([EMCC, '-O2', 'tests/hello_world.cpp'], '') + output = self.check_working([EMCC, '-O2', '--closure', '1', 'tests/hello_world.cpp'], '') assert os.path.exists('a.out.js') def test_llvm(self): @@ -11518,7 +11656,7 @@ fi try_delete(basebc_name) # we might need to check this file later try_delete(dcebc_name) # we might need to check this file later for ll_name in ll_names: try_delete(ll_name) - output = self.do([EMCC, '-O' + str(i), '--closure', '0', '-s', 'RELOOP=0', '--llvm-lto', '0', path_from_root('tests', filename)]) + output = self.do([EMCC, '-O' + str(i), '-s', 'RELOOP=0', '--llvm-lto', '0', path_from_root('tests', filename)]) #print output assert INCLUDING_MESSAGE.replace('X', libname) in output if libname == 'libc': @@ -11566,7 +11704,7 @@ fi print >> sys.stderr, phase, i opt = min(i, 2) try_delete('a.out.js') - output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop.cpp'), '-O' + str(opt), '--closure', '0'], + output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop.cpp'), '-O' + str(opt)], stdout=PIPE, stderr=PIPE).communicate() self.assertContained('hello, world!', run_js('a.out.js')) output = '\n'.join(output) @@ -11626,7 +11764,7 @@ fi ]: print >> sys.stderr, args, input_file, expect_pre_save, expect_pre_load, expect_funcs_save, expect_funcs_load, expect_jsfuncs_save, expect_jsfuncs_load, expected self.clear() - out, err = Popen([PYTHON, EMCC, '-O2', '--closure', '0', path_from_root('tests', input_file)] + args, stdout=PIPE, stderr=PIPE).communicate() + out, err = Popen([PYTHON, EMCC, '-O2', path_from_root('tests', input_file)] + args, stdout=PIPE, stderr=PIPE).communicate() errtail = err.split('emcc invocation')[-1] self.assertContained('hello, world!', run_js('a.out.js'), errtail) assert (PRE_SAVE_MSG in err) == expect_pre_save, errtail diff --git a/tools/file_packager.py b/tools/file_packager.py index 7e196efd..bfa8e2f0 100644 --- a/tools/file_packager.py +++ b/tools/file_packager.py @@ -228,7 +228,7 @@ if has_preloaded: curr = open(file_['localname'], 'rb').read() file_['data_end'] = start + len(curr) if AV_WORKAROUND: curr += '\x00' - print >> sys.stderr, 'bundling', file_['name'], file_['localname'], file_['data_start'], file_['data_end'] + #print >> sys.stderr, 'bundling', file_['name'], file_['localname'], file_['data_start'], file_['data_end'] start += len(curr) data.write(curr) data.close() diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index efbfa8aa..f2dc516a 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -1598,7 +1598,7 @@ function registerize(ast) { fun[2].push(reg); } } - getStatements(fun).unshift(['var', vars]); + if (vars.length > 0) getStatements(fun).unshift(['var', vars]); } } else { //printErr('unfake params: \n\n' + astToSrc(fun) + '\n\n'); diff --git a/tools/shared.py b/tools/shared.py index 401a580b..0282fbb1 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -566,6 +566,7 @@ class Settings: if opt_level >= 2: Settings.RELOOP = 1 if opt_level >= 3: + # Aside from these, -O3 also runs closure compiler Settings.INLINING_LIMIT = 0 Settings.DOUBLE_MODE = 0 Settings.PRECISE_I64_MATH = 0 diff --git a/tools/test-js-optimizer-regs-output.js b/tools/test-js-optimizer-regs-output.js index 36006b7c..fe7de5fb 100644 --- a/tools/test-js-optimizer-regs-output.js +++ b/tools/test-js-optimizer-regs-output.js @@ -225,4 +225,8 @@ function switchey(r1) { r9 = r1 + 2; pp(r9); } +function __ZN14NetworkAddressC1EPKcti(r1) { + __ZN14NetworkAddressC2EPKcti(r1); + return; +} diff --git a/tools/test-js-optimizer-regs.js b/tools/test-js-optimizer-regs.js index 4802afa3..3013e518 100644 --- a/tools/test-js-optimizer-regs.js +++ b/tools/test-js-optimizer-regs.js @@ -230,4 +230,8 @@ function switchey(x) { var aaa = x+2; pp(aaa); } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["test", "primes", "atomic", "fcntl_open", "ex", "switchey"] +function __ZN14NetworkAddressC1EPKcti($this) { + __ZN14NetworkAddressC2EPKcti($this); + return; +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["test", "primes", "atomic", "fcntl_open", "ex", "switchey", "__ZN14NetworkAddressC1EPKcti"] |