diff --git a/AUTHORS b/AUTHORS index 6a28c2054491d..6cf860aba7867 100644 --- a/AUTHORS +++ b/AUTHORS @@ -108,3 +108,9 @@ a license to everyone to use it as detailed in LICENSE.) * Ben Noordhuis * Bob Roberts * John Vilk +* Daniel Baulig (copyright owned by Facebook, Inc.) +* Lu Wang +* Heidi Pan (copyright owned by Intel) +* Vasilis Kalintiris +* Adam C. Clifton +* Volo Zyko diff --git a/ChangeLog b/ChangeLog index 94a24675d1ce6..cab1d74a4aedf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,8 +9,32 @@ Not all changes are documented here. In particular, new features, user-oriented Current trunk code ------------------ - - To see a list of commits that in the active development branch 'incoming', which have not yet been packaged in a release, see - https://github.com/kripken/emscripten/compare/1.7.5...incoming + - To see a list of commits in the active development branch 'incoming', which have not yet been packaged in a release, see + https://github.com/kripken/emscripten/compare/1.7.8...incoming + +v1.7.8: 11/19/2013 +------------------ + - Fixed an issue with -MMD compilation parameter. + - Added EM_ASM_INT() and EM_ASM_DOUBLE() macros. For more information, read https://groups.google.com/forum/#!topic/emscripten-discuss/BFGTJPCgO6Y . + - Fixed --split parameter to also work on Windows. + - Fixed issues with BSD sockets accept() call. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.7.7...1.7.8 + +v1.7.7: 11/16/2013 +------------------ + - Improve SDL audio buffer queue timing support. + - Improved default precision of clock_gettime even when not using CLOCK_REALTIME. + - Optimize and fix issues with LLVM IR processing. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.7.6...1.7.7 + +v1.7.6: 11/15/2013 +------------------ + - Added regex implementation from musl libc. + - The command line parameter -s DEAD_FUNCTIONS=[] can now be used to explicitly kill functions coming from built-in library_xx.js. + - Improved EGL support and GLES2 spec conformance. + - Reverted -s TOTAL_MEMORY=x to require pow2 values, instead of the relaxed 'multiples of 16MB'. This is because the relaxed rule is released only in Firefox 26 which which is currently in Beta and ships on the week of December 10th (currently in Beta). As of writing, current stable Firefox 25 does not yet support these. + - Adjusted the default linker behavior to warn about all missing symbols, instead of silently ignoring them. Use -s WARN_ON_UNDEFINED_SYMBOLS=0 to suppress these warnings if necessary. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.7.5...1.7.6 v1.7.5: 11/13/2013 ------------------ diff --git a/cmake/Platform/Emscripten.cmake b/cmake/Platform/Emscripten.cmake index c30632cafbde1..ef813eb800a25 100644 --- a/cmake/Platform/Emscripten.cmake +++ b/cmake/Platform/Emscripten.cmake @@ -109,6 +109,8 @@ set(CMAKE_CXX_RESPONSE_FILE_LINK_FLAG "@") # Specify the program to use when building static libraries. Force Emscripten-related command line options to clang. set(CMAKE_CXX_ARCHIVE_CREATE "${CMAKE_AR} rc ${CMAKE_START_TEMP_FILE} ${CMAKE_END_TEMP_FILE}") set(CMAKE_C_ARCHIVE_CREATE "${CMAKE_AR} rc ${CMAKE_START_TEMP_FILE} ${CMAKE_END_TEMP_FILE}") +set(CMAKE_CXX_ARCHIVE_APPEND "${CMAKE_AR} r ${CMAKE_START_TEMP_FILE} ${CMAKE_END_TEMP_FILE}") +set(CMAKE_C_ARCHIVE_APPEND "${CMAKE_AR} r ${CMAKE_START_TEMP_FILE} ${CMAKE_END_TEMP_FILE}") # Set a global EMSCRIPTEN variable that can be used in client CMakeLists.txt to detect when building using Emscripten. # There seems to be some kind of bug with CMake, so you might need to define this manually on the command line with "-DEMSCRIPTEN=1". diff --git a/emcc b/emcc index 6a483a39c85fb..0f4f8800ef4e5 100755 --- a/emcc +++ b/emcc @@ -53,13 +53,17 @@ from tools import shared, jsrun from tools.shared import Compression, execute, suffix, unsuffixed, unsuffixed_basename, WINDOWS from tools.response_file import read_response_file -CXX_SUFFIXES = ('.cpp', '.cxx', '.cc') -SOURCE_SUFFIXES = ('.c', '.cpp', '.cxx', '.cc', '.m', '.mm') -BITCODE_SUFFIXES = ('.bc', '.o', '.obj') -DYNAMICLIB_SUFFIXES = ('.dylib', '.so', '.dll') -STATICLIB_SUFFIXES = ('.a',) -ASSEMBLY_SUFFIXES = ('.ll',) +# endings = dot + a suffix, safe to test by filename.endswith(endings) +C_ENDINGS = ('.c', '.C') +CXX_ENDINGS = ('.cpp', '.cxx', '.cc', '.CPP', '.CXX', '.CC') +SOURCE_ENDINGS = C_ENDINGS + CXX_ENDINGS + ('.m', '.mm') +BITCODE_ENDINGS = ('.bc', '.o', '.obj') +DYNAMICLIB_ENDINGS = ('.dylib', '.so', '.dll') +STATICLIB_ENDINGS = ('.a',) +ASSEMBLY_ENDINGS = ('.ll',) + LIB_PREFIXES = ('', 'lib') + JS_CONTAINING_SUFFIXES = ('js', 'html') # Mapping of emcc opt levels to llvm opt levels. We use llvm opt level 3 in emcc opt @@ -340,6 +344,10 @@ Options that are modified or new in %s include: For more docs on the options --preload-file accepts, see https://github.com/kripken/emscripten/wiki/Filesystem-Guide + --exclude-file Files and directories to be excluded from + --embed-file and --preload-file + wildcard is supported + --compression Compress both the compiled code and embedded/ preloaded files. should be a triple, @@ -498,6 +506,17 @@ Options that are modified or new in %s include: --proxy-to-worker Generates both html and js files. The main program is in js, and the html proxies to/from it. + --emrun Enables the generated output to be aware of the + emrun command line tool. This allows stdout, stderr + and exit(returncode) capture when running the + generated application through emrun. + + --em-config Specifies the location of the .emscripten configuration + file for the current compiler run. If not specified, + the environment variable EM_CONFIG is read for this + file, and if that is not set, the default location + ~/.emscripten is assumed. + The target file, if specified (-o ), defines what will be generated: @@ -746,9 +765,13 @@ def in_temp(name): def filename_type_suffix(filename): for i in reversed(filename.split('.')[1:]): if not i.isdigit(): - return '.' + i + return i return '' +def filename_type_ending(filename): + suffix = filename_type_suffix(filename) + return '' if not suffix else ('.' + suffix) + try: call = CXX if use_cxx else CC @@ -768,11 +791,13 @@ try: split_js_file = None preload_files = [] embed_files = [] + exclude_files = [] compression = None ignore_dynamic_linking = False shell_path = shared.path_from_root('src', 'shell.html') js_libraries = [] bind = False + emrun = False jcache = False save_bc = False memory_init_file = False @@ -883,6 +908,11 @@ try: preload_files.append(newargs[i+1]) newargs[i] = '' newargs[i+1] = '' + elif newargs[i].startswith('--exclude-file'): + check_bad_eq(newargs[i]) + exclude_files.append(newargs[i+1]) + newargs[i] = '' + newargs[i+1] = '' elif newargs[i].startswith('--compression'): check_bad_eq(newargs[i]) parts = newargs[i+1].split(',') @@ -962,12 +992,24 @@ try: if not absolute_warning_shown and os.path.isabs(path_name): logging.warning ('-I or -L of an absolute path "' + newargs[i] + '" encountered. If this is to a local system header/library, it may cause problems (local system files make sense for compiling natively on your system, but not necessarily to JavaScript). Pass \'-Wno-warn-absolute-paths\' to emcc to hide this warning.') # Of course an absolute path to a non-system-specific library or header is fine, and you can ignore this warning. The danger are system headers that are e.g. x86 specific and nonportable. The emscripten bundled headers are modified to be portable, local system ones are generally not absolute_warning_shown = True + elif newargs[i] == '--emrun': + emrun = True + newargs[i] = '' + elif newargs[i] == '--em-config': + # This option is parsed in tools/shared.py, here only clean it up from being passed to clang. + newargs[i] = '' + newargs[i+1] = '' + newargs = [ arg for arg in newargs if arg is not '' ] # If user did not specify a default -std for C++ code, specify the emscripten default. if default_cxx_std: newargs = newargs + [default_cxx_std] + if emrun: + pre_js += open(shared.path_from_root('src', 'emrun_prejs.js')).read() + '\n' + post_js += open(shared.path_from_root('src', 'emrun_postjs.js')).read() + '\n' + if js_opts is None: js_opts = opt_level >= 2 if llvm_opts is None: llvm_opts = LLVM_OPT_LEVEL[opt_level] if llvm_lto is None and opt_level >= 3: llvm_lto = 3 @@ -1018,23 +1060,23 @@ try: prev = newargs[i-1] if prev in ['-MT', '-MF', '-MQ', '-D', '-U', '-o', '-x', '-Xpreprocessor', '-include', '-imacros', '-idirafter', '-iprefix', '-iwithprefix', '-iwithprefixbefore', '-isysroot', '-imultilib', '-A', '-isystem', '-iquote', '-install_name', '-compatibility_version', '-current_version', '-I', '-L']: continue # ignore this gcc-style argument - if (os.path.islink(arg) and os.path.realpath(arg).endswith(SOURCE_SUFFIXES + BITCODE_SUFFIXES + DYNAMICLIB_SUFFIXES + ASSEMBLY_SUFFIXES)): + if (os.path.islink(arg) and os.path.realpath(arg).endswith(SOURCE_ENDINGS + BITCODE_ENDINGS + DYNAMICLIB_ENDINGS + ASSEMBLY_ENDINGS)): arg = os.path.realpath(arg) if not arg.startswith('-'): if not os.path.exists(arg): - logging.error(arg + ': No such file or directory') + logging.error('%s: No such file or directory ("%s" was expected to be an input file, based on the commandline arguments provided)' % (arg, arg)) exit(1) - arg_suffix = filename_type_suffix(arg) - if arg_suffix.endswith(SOURCE_SUFFIXES + BITCODE_SUFFIXES + DYNAMICLIB_SUFFIXES + ASSEMBLY_SUFFIXES) or shared.Building.is_ar(arg): # we already removed -o , so all these should be inputs + arg_ending = filename_type_ending(arg) + if arg_ending.endswith(SOURCE_ENDINGS + BITCODE_ENDINGS + DYNAMICLIB_ENDINGS + ASSEMBLY_ENDINGS) or shared.Building.is_ar(arg): # we already removed -o , so all these should be inputs newargs[i] = '' - if arg_suffix.endswith(SOURCE_SUFFIXES): + if arg_ending.endswith(SOURCE_ENDINGS): input_files.append(arg) has_source_inputs = True - elif arg_suffix.endswith(ASSEMBLY_SUFFIXES) or shared.Building.is_bitcode(arg): # this should be bitcode, make sure it is valid + elif arg_ending.endswith(ASSEMBLY_ENDINGS) or shared.Building.is_bitcode(arg): # this should be bitcode, make sure it is valid input_files.append(arg) - elif arg_suffix.endswith(STATICLIB_SUFFIXES + DYNAMICLIB_SUFFIXES): + elif arg_ending.endswith(STATICLIB_ENDINGS + DYNAMICLIB_ENDINGS): # if it's not, and it's a library, just add it to libs to find later l = unsuffixed_basename(arg) for prefix in LIB_PREFIXES: @@ -1046,10 +1088,10 @@ try: newargs[i] = '' else: logging.warning(arg + ' is not valid LLVM bitcode') - elif arg_suffix.endswith(STATICLIB_SUFFIXES): + elif arg_ending.endswith(STATICLIB_ENDINGS): if not shared.Building.is_ar(arg): if shared.Building.is_bitcode(arg): - logging.error(arg + ': File has a suffix of a static library ' + str(STATICLIB_SUFFIXES) + ', but instead is an LLVM bitcode file! When linking LLVM bitcode files, use one of the suffixes ' + str(BITCODE_SUFFIXES)) + logging.error(arg + ': File has a suffix of a static library ' + str(STATICLIB_ENDINGS) + ', but instead is an LLVM bitcode file! When linking LLVM bitcode files, use one of the suffixes ' + str(BITCODE_ENDINGS)) else: logging.error(arg + ': Unknown format, not a static library!') exit(1) @@ -1074,17 +1116,12 @@ try: target = target_basename + '.o' final_suffix = 'o' - # do not link in libs when just generating object code (not an 'executable', i.e. JS, or a library) - if ('.' + final_suffix) in BITCODE_SUFFIXES and len(libs) > 0: - logging.warning('not linking against libraries since only compiling to bitcode') - libs = [] - # Find library files for lib in libs: logging.debug('looking for library "%s"' % lib) found = False for prefix in LIB_PREFIXES: - for suff in STATICLIB_SUFFIXES + DYNAMICLIB_SUFFIXES: + for suff in STATICLIB_ENDINGS + DYNAMICLIB_ENDINGS: name = prefix + lib + suff for lib_dir in lib_dirs: path = os.path.join(lib_dir, name) @@ -1095,12 +1132,21 @@ try: break if found: break if found: break - - if ignore_dynamic_linking: - input_files = filter(lambda input_file: not input_file.endswith(DYNAMICLIB_SUFFIXES), input_files) + if not found: logging.warning('emcc: cannot find library "%s"' % lib) + + # If not compiling to JS, then we are compiling to an intermediate bitcode objects or library, so + # ignore dynamic linking, since multiple dynamic linkings can interfere with each other + if not filename_type_suffix(target) in JS_CONTAINING_SUFFIXES or ignore_dynamic_linking: + def check(input_file): + if filename_type_ending(input_file) in DYNAMICLIB_ENDINGS: + if not ignore_dynamic_linking: logging.warning('ignoring dynamic library %s because not compiling to JS or HTML, remember to link it when compiling to JS or HTML at the end' % os.path.basename(input_file)) + return False + else: + return True + input_files = filter(lambda input_file: check(input_file), input_files) if len(input_files) == 0: - logging.error('no input files\nnote that input files without a known suffix are ignored, make sure your input files end with one of: ' + str(SOURCE_SUFFIXES + BITCODE_SUFFIXES + DYNAMICLIB_SUFFIXES + STATICLIB_SUFFIXES + ASSEMBLY_SUFFIXES)) + logging.error('no input files\nnote that input files without a known suffix are ignored, make sure your input files end with one of: ' + str(SOURCE_ENDINGS + BITCODE_ENDINGS + DYNAMICLIB_ENDINGS + STATICLIB_ENDINGS + ASSEMBLY_ENDINGS)) exit(0) newargs = CC_ADDITIONAL_ARGS + newargs @@ -1127,8 +1173,28 @@ try: logging.warning('disabling asm.js since embind is not ready for it yet') shared.Settings.ASM_JS = 0 + if os.environ.get('EMCC_FAST_COMPILER'): + shared.Settings.ASM_JS = 1 + if shared.Settings.DISABLE_EXCEPTION_CATCHING == 0: + logging.warning('disabling exception catching since not supported in fastcomp yet') + shared.Settings.DISABLE_EXCEPTION_CATCHING = 1 + assert shared.Settings.ALLOW_MEMORY_GROWTH == 0, 'memory growth not supported in fastcomp yet' + assert shared.Settings.UNALIGNED_MEMORY == 0, 'forced unaligned memory not supported in fastcomp' + assert shared.Settings.SAFE_HEAP == 0, 'safe heap not supported in fastcomp yet' + assert shared.Settings.CHECK_HEAP_ALIGN == 0, 'check heap align not supported in fastcomp yet' + assert shared.Settings.SAFE_DYNCALLS == 0, 'safe dyncalls not supported in fastcomp' + assert shared.Settings.RESERVED_FUNCTION_POINTERS == 0, 'reserved function pointers not supported in fastcomp' + assert shared.Settings.ASM_HEAP_LOG == 0, 'asm heap log not supported in fastcomp' + assert shared.Settings.LABEL_DEBUG == 0, 'label debug not supported in fastcomp' + assert shared.Settings.LEGACY_GL_EMULATION == 0, 'legacy gl emulation not supported in fastcomp' + assert shared.Settings.EXECUTION_TIMEOUT == -1, 'execution timeout not supported in fastcomp' + assert shared.Settings.NAMED_GLOBALS == 0, 'named globals not supported in fastcomp' + assert shared.Settings.PGO == 0, 'pgo not supported in fastcomp' + assert shared.Settings.TARGET_LE32 == 1, 'fastcomp requires le32' + assert not bind, 'embind not supported in fastcomp yet' + if shared.Settings.ASM_JS: - assert opt_level >= 1, 'asm.js requires -O1 or above' + assert opt_level >= 1 or os.environ.get('EMCC_FAST_COMPILER'), 'asm.js requires -O1 or above' if bind: shared.Settings.RESERVED_FUNCTION_POINTERS = max(shared.Settings.RESERVED_FUNCTION_POINTERS, 10) @@ -1142,11 +1208,10 @@ try: heap = 4096 while heap < shared.Settings.TOTAL_MEMORY: - heap *= 2 - #if heap <= 16*1024*1024: - # heap *= 2 - #else: - # heap += 16*1024*1024 + if heap < 16*1024*1024: + heap *= 2 + else: + heap += 16*1024*1024 if heap != shared.Settings.TOTAL_MEMORY: logging.warning('increasing TOTAL_MEMORY to %d to be more reasonable for asm.js' % heap) shared.Settings.TOTAL_MEMORY = heap @@ -1218,14 +1283,14 @@ try: # First, generate LLVM bitcode. For each input file, we get base.o with bitcode for input_file in input_files: - file_suffix = filename_type_suffix(input_file) - if file_suffix.endswith(SOURCE_SUFFIXES): + file_ending = filename_type_ending(input_file) + if file_ending.endswith(SOURCE_ENDINGS): logging.debug('compiling source file: ' + input_file) input_file = shared.Building.preprocess(input_file, in_temp(uniquename(input_file))) output_file = in_temp(unsuffixed(uniquename(input_file)) + '.o') temp_files.append(output_file) args = newargs + ['-emit-llvm', '-c', input_file, '-o', output_file] - if file_suffix.endswith(CXX_SUFFIXES): + if file_ending.endswith(CXX_ENDINGS): args += shared.EMSDK_CXX_OPTS logging.debug("running: " + call + ' ' + ' '.join(args)) execute([call] + args) # let compiler frontend print directly, so colors are saved (PIPE kills that) @@ -1233,17 +1298,17 @@ try: logging.error('compiler frontend failed to generate LLVM bitcode, halting') sys.exit(1) else: # bitcode - if file_suffix.endswith(BITCODE_SUFFIXES): + if file_ending.endswith(BITCODE_ENDINGS): logging.debug('copying bitcode file: ' + input_file) temp_file = in_temp(unsuffixed(uniquename(input_file)) + '.o') shutil.copyfile(input_file, temp_file) temp_files.append(temp_file) - elif file_suffix.endswith(DYNAMICLIB_SUFFIXES) or shared.Building.is_ar(input_file): + elif file_ending.endswith(DYNAMICLIB_ENDINGS) or shared.Building.is_ar(input_file): logging.debug('copying library file: ' + input_file) temp_file = in_temp(uniquename(input_file)) shutil.copyfile(input_file, temp_file) temp_files.append(temp_file) - elif file_suffix.endswith(ASSEMBLY_SUFFIXES): + elif file_ending.endswith(ASSEMBLY_ENDINGS): if not LEAVE_INPUTS_RAW: # Note that by assembling the .ll file, then disassembling it later, we will # remove annotations which is a good thing for compilation time @@ -1261,8 +1326,8 @@ try: # Optimize source files if llvm_opts > 0: for i, input_file in enumerate(input_files): - file_suffix = filename_type_suffix(input_file) - if file_suffix.endswith(SOURCE_SUFFIXES): + file_ending = filename_type_ending(input_file) + if file_ending.endswith(SOURCE_ENDINGS): temp_file = temp_files[i] logging.debug('optimizing %s with -O%s' % (input_file, llvm_opts)) shared.Building.llvm_opt(temp_file, llvm_opts) @@ -1607,11 +1672,11 @@ try: # First, combine the bitcode files if there are several. We must also link if we have a singleton .a if len(input_files) + len(extra_files_to_link) > 1 or \ - (not LEAVE_INPUTS_RAW and not (suffix(temp_files[0]) in BITCODE_SUFFIXES or suffix(temp_files[0]) in DYNAMICLIB_SUFFIXES) and shared.Building.is_ar(temp_files[0])): + (not LEAVE_INPUTS_RAW and not (suffix(temp_files[0]) in BITCODE_ENDINGS or suffix(temp_files[0]) in DYNAMICLIB_ENDINGS) and shared.Building.is_ar(temp_files[0])): linker_inputs = temp_files + extra_files_to_link logging.debug('linking: ' + str(linker_inputs)) t0 = time.time() - shared.Building.link(linker_inputs, in_temp(target_basename + '.bc'), force_archive_contents = len(filter(lambda temp: not temp.endswith(STATICLIB_SUFFIXES), temp_files)) == 0) + shared.Building.link(linker_inputs, in_temp(target_basename + '.bc'), force_archive_contents = len(filter(lambda temp: not temp.endswith(STATICLIB_ENDINGS), temp_files)) == 0) t1 = time.time() logging.debug(' linking took %.2f seconds' % (t1 - t0)) final = in_temp(target_basename + '.bc') @@ -1656,7 +1721,12 @@ try: else: # At minimum remove dead functions etc., this potentially saves a lot in the size of the generated code (and the time to compile it) link_opts += shared.Building.get_safe_internalize() + ['-globaldce'] - if not save_bc: + + # Simplify LLVM bitcode for fastcomp + if os.environ.get('EMCC_FAST_COMPILER') and not AUTODEBUG: + link_opts += ['-pnacl-abi-simplify-preopt', '-pnacl-abi-simplify-postopt'] + + if (not save_bc and not os.environ.get('EMCC_FAST_COMPILER')) or AUTODEBUG: # let llvm opt directly emit ll, to skip writing and reading all the bitcode link_opts += ['-S'] shared.Building.llvm_opt(final, link_opts, final + '.link.ll') @@ -1683,6 +1753,12 @@ try: final += '.ad.ll' if DEBUG: save_intermediate('autodebug', 'll') + # Simplify bitcode after autodebug + if os.environ.get('EMCC_FAST_COMPILER') and (AUTODEBUG or LEAVE_INPUTS_RAW): + shared.Building.llvm_opt(final, ['-pnacl-abi-simplify-preopt', '-pnacl-abi-simplify-postopt'], final + '.adsimp.bc') + final += '.adsimp.bc' + if DEBUG: save_intermediate('adsimp', 'bc') + # Emscripten logging.debug('LLVM => JS') extra_args = [] if not js_libraries else ['--libraries', ','.join(map(os.path.abspath, js_libraries))] @@ -1693,13 +1769,16 @@ try: # Embed and preload files if len(preload_files) + len(embed_files) > 0: logging.debug('setting up files') - file_args = ['--pre-run'] + file_args = [] if len(preload_files) > 0: file_args.append('--preload') file_args += preload_files if len(embed_files) > 0: file_args.append('--embed') file_args += embed_files + if len(exclude_files) > 0: + file_args.append('--exclude') + file_args += exclude_files if Compression.on: file_args += ['--compress', Compression.encoder, Compression.decoder, Compression.js_name] if use_preload_cache: diff --git a/emmake b/emmake index 18e6afa9b2e05..e8f34f416cdb4 100755 --- a/emmake +++ b/emmake @@ -6,7 +6,7 @@ the environment variables to use emcc and so forth. Usage: emmake make [FLAGS] -Not that if you ran configure with emconfigure, then +Note that if you ran configure with emconfigure, then the environment variables have already been detected and set. This script is useful if you have no configure step, and your Makefile uses the environment vars diff --git a/emrun b/emrun new file mode 100755 index 0000000000000..d5975b8ae59b0 --- /dev/null +++ b/emrun @@ -0,0 +1,1083 @@ +#!/usr/bin/env python + +# emrun: Implements machinery that allows running a .html page as if it was a standard executable file. +# Usage: emrun filename.html +# See emrun --help for more information + +import os, platform, optparse, logging, re, pprint, atexit, urlparse, subprocess, sys, SocketServer, BaseHTTPServer, SimpleHTTPServer, time, string, struct, socket, cgi +from operator import itemgetter +from urllib import unquote +from Queue import PriorityQueue +from threading import Thread, RLock + +# Populated from cmdline params +emrun_options = None + +# Represents the process object handle to the browser we opened to run the html page. +browser_process = None + +# If we have routed browser output to file with --log_stdout and/or --log_stderr, +# these track the handles. +browser_stdout_handle = sys.stdout +browser_stderr_handle = sys.stderr + +# This flag tracks whether the html page has sent any stdout messages back to us. +# Used to detect whether we might have gotten detached from the browser process we +# spawned, in which case we are not able to detect when user closes the browser with +# the close button. +have_received_messages = False + +# At startup print a warning message once if user did not build with --emrun. +emrun_not_enabled_nag_printed = False + +# Stores the exit() code of the html page when/if it quits. +page_exit_code = 0 + +# If this is set to a non-empty string, all processes by this name will be killed at exit. +# This is used to clean up after browsers that spawn subprocesses to handle the actual +# browser launch. For example opera has a launcher.exe that runs the actual opera browser. +# So killing browser_process would just kill launcher.exe and not the opera browser itself. +processname_killed_atexit = "" + +# If user does not specify a --port parameter, this port is used to launch the server. +default_webserver_port = 6931 + +# Location of Android Debug Bridge executable +ADB = '' + +# Host OS detection to autolocate browsers and other OS-specific support needs. +WINDOWS = False +LINUX = False +OSX = False +if os.name == 'nt': + WINDOWS = True + + import win32api, _winreg + from win32com.client import GetObject + from win32api import GetFileVersionInfo, LOWORD, HIWORD + import shlex + from _winreg import HKEY_CURRENT_USER, OpenKey, QueryValue + +elif platform.system() == 'Linux': + LINUX = True +elif platform.mac_ver()[0] != '': + OSX = True + + import plistlib + +# If you are running on an OS that is not any of these, must add explicit support for it. +if not WINDOWS and not LINUX and not OSX: + raise Exception("Unknown OS!") + +# Absolute wallclock time in seconds specifying when the previous HTTP stdout message from +# the page was received. +last_message_time = time.clock() + +# Absolute wallclock time in seconds telling when we launched emrun. +page_start_time = time.clock() + +# Stores the time of most recent http page serve. +page_last_served_time = None + +# Returns given log message formatted to be outputted on a HTML page. +def format_html(msg): + if not msg.endswith('\n'): + msg += '\n' + msg = cgi.escape(msg) + msg = msg.replace('\r\n', '
').replace('\n', '
') + return msg + +# HTTP requests are handled from separate threads - synchronize them to avoid race conditions +http_mutex = RLock() + +# Prints a log message to 'info' stdout channel. Always printed. +def logi(msg): + global last_message_time + with http_mutex: + if emrun_options.log_html: + sys.stdout.write(format_html(msg)) + else: + print >> sys.stdout, msg + sys.stdout.flush() + last_message_time = time.clock() + +# Prints a verbose log message to stdout channel. Only shown if run with --verbose. +def logv(msg): + global emrun_options, last_message_time + with http_mutex: + if emrun_options.verbose: + if emrun_options.log_html: + sys.stdout.write(format_html(msg)) + else: + print >> sys.stdout, msg + sys.stdout.flush() + last_message_time = time.clock() + +# Prints an error message to stderr channel. +def loge(msg): + global last_message_time + with http_mutex: + if emrun_options.log_html: + sys.stderr.write(format_html(msg)) + else: + print >> sys.stderr, msg + sys.stderr.flush() + last_message_time = time.clock() + +def format_eol(msg): + if WINDOWS: + msg = msg.replace('\r\n', '\n').replace('\n', '\r\n') + return msg + +# Prints a message to the browser stdout output stream. +def browser_logi(msg): + global browser_stdout_handle + msg = format_eol(msg) + print >> browser_stdout_handle, msg + browser_stdout_handle.flush() + last_message_time = time.clock() + +# Prints a message to the browser stderr output stream. +def browser_loge(msg): + global browser_stderr_handle + msg = format_eol(msg) + print >> browser_stderr_handle, msg + browser_stderr_handle.flush() + last_message_time = time.clock() + +# Unquotes a unicode string. (translates ascii-encoded utf string back to utf) +def unquote_u(source): + result = unquote(source) + if '%u' in result: + result = result.replace('%u','\\u').decode('unicode_escape') + return result + +# Returns whether the browser page we spawned is still running. +# (note, not perfect atm, in case we are running in detached mode) +def is_browser_process_alive(): + return browser_process and browser_process.poll() == None + +# Kills browser_process and processname_killed_atexit. +def kill_browser_process(): + global browser_process, processname_killed_atexit, emrun_options, ADB + if browser_process: + try: + logv('Terminating browser process..') + browser_process.kill() + except: + pass + browser_process = None + if len(processname_killed_atexit) > 0: + if emrun_options.android: + logv("Terminating Android app '" + processname_killed_atexit + "'.") + subprocess.call([ADB, 'shell', 'am', 'force-stop', processname_killed_atexit]) + else: + logv("Terminating all processes that have string '" + processname_killed_atexit + "' in their name.") + if WINDOWS: + process_image = processname_killed_atexit if '.exe' in processname_killed_atexit else (processname_killed_atexit + '.exe') + process = subprocess.Popen(['taskkill', '/F', '/IM', process_image, '/T'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + process.communicate() + else: + try: + subprocess.call(['pkill', processname_killed_atexit]) + except OSError, e: + try: + subprocess.call(['killall', processname_killed_atexit]) + except OSError, e: + loge('Both commands pkill and killall failed to clean up the spawned browser process. Perhaps neither of these utilities is available on your system?') + # Clear the process name to represent that the browser is now dead. + processname_killed_atexit = '' + +# Our custom HTTP web server that will server the target page to run via .html. +# This is used so that we can load the page via a http:// URL instead of a file:// URL, since those wouldn't work too well unless user allowed XHR without CORS rules. +# Also, the target page will route its stdout and stderr back to here via HTTP requests. +class HTTPWebServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): + # Log messaging arriving via HTTP can come in out of sequence. Implement a sequencing mechanism to enforce ordered transmission. + expected_http_seq_num = -1 + # Stores messages that have arrived out of order, pending for a send as soon as the missing message arrives. + # Kept in sorted order, first element is the oldest message received. + http_message_queue = [] + + def handle_incoming_message(self, seq_num, log, data): + global have_received_messages + with http_mutex: + have_received_messages = True + + if self.expected_http_seq_num == -1: + self.expected_http_seq_num = seq_num+1 + log(data) + elif seq_num == -1: # Message arrived without a sequence number? Just log immediately + log(data) + elif seq_num == self.expected_http_seq_num: + log(data) + self.expected_http_seq_num += 1 + self.print_messages_due() + elif seq_num < self.expected_http_seq_num: + log(data) + else: + self.http_message_queue += [(seq_num, data, log)] + self.http_message_queue.sort(key=itemgetter(0)) + if len(self.http_message_queue) > 16: + self.print_next_message() + + # If it's been too long since we we got a message, prints out the oldest queued message, ignoring the proper order. + # This ensures that if any messages are actually lost, that the message queue will be orderly flushed. + def print_timed_out_messages(self): + global last_message_time + with http_mutex: + now = time.clock() + max_message_queue_time = 5 + if len(self.http_message_queue) > 0 and now - last_message_time > max_message_queue_time: + self.print_next_message() + + # Skips to printing the next message in queue now, independent of whether there was missed messages in the sequence numbering. + def print_next_message(self): + with http_mutex: + if len(self.http_message_queue) > 0: + self.expected_http_seq_num = self.http_message_queue[0][0] + self.print_messages_due() + + # Completely flushes all out-of-order messages in the queue. + def print_all_messages(self): + with http_mutex: + while len(self.http_message_queue) > 0: + self.print_next_message() + + # Prints any messages that are now due after we logged some other previous messages. + def print_messages_due(self): + with http_mutex: + while len(self.http_message_queue) > 0: + msg = self.http_message_queue[0] + if msg[0] == self.expected_http_seq_num: + msg[2](msg[1]) + self.expected_http_seq_num += 1 + self.http_message_queue.pop(0) + else: + return + + def serve_forever(self, timeout=0.5): + global emrun_options, last_message_time, page_exit_code, have_received_messages, emrun_not_enabled_nag_printed + self.is_running = True + self.timeout = timeout + while self.is_running: + now = time.clock() + # Did user close browser? + if not emrun_options.serve_after_close and browser_process and browser_process.poll() != None: + if not have_received_messages: + emrun_options.serve_after_close = True + logv('Warning: emrun got detached from the target browser process. Cannot detect when user closes the browser. Behaving as if --serve_after_close was passed in.') + else: + self.shutdown() + logv('Browser process has quit. Shutting down web server.. Pass --serve_after_close to keep serving the page even after the browser closes.') + + # Serve HTTP + self.handle_request() + # Process message log queue + self.print_timed_out_messages() + + # If web page was silent for too long without printing anything, kill process. + time_since_message = now - last_message_time + if emrun_options.silence_timeout != 0 and time_since_message > emrun_options.silence_timeout: + self.shutdown() + logv('No activity in ' + str(emrun_options.silence_timeout) + ' seconds. Quitting web server with return code ' + str(emrun_options.timeout_returncode) + '. (--silence_timeout option)') + page_exit_code = emrun_options.timeout_returncode + emrun_options.kill_on_exit = True + + # If the page has been running too long as a whole, kill process. + time_since_start = now - page_start_time + if emrun_options.timeout != 0 and time_since_start > emrun_options.timeout: + self.shutdown() + logv('Page has not finished in ' + str(emrun_options.timeout) + ' seconds. Quitting web server with return code ' + str(emrun_options.timeout_returncode) + '. (--timeout option)') + emrun_options.kill_on_exit = True + page_exit_code = emrun_options.timeout_returncode + + # If we detect that the page is not running with emrun enabled, print a warning message. + if not emrun_not_enabled_nag_printed and page_last_served_time is not None: + time_since_page_serve = now - page_last_served_time + if not have_received_messages and time_since_page_serve > 10: + logi('The html page you are running is not emrun-capable. Stdout, stderr and exit(returncode) capture will not work. Recompile the application with the --emrun linker flag to enable this, or pass --no_emrun_detect to emrun to hide this check.') + emrun_not_enabled_nag_printed = True + + # Clean up at quit, print any leftover messages in queue. + self.print_all_messages() + + def handle_error(self, request, client_address): + err = sys.exc_info()[1][0] + # Filter out the useless '[Errno 10054] An existing connection was forcibly closed by the remote host' errors that occur when we + # forcibly kill the client. + if err != 10054: + SocketServer.BaseServer.handle_error(self, request, client_address) + + def shutdown(self): + self.is_running = False + self.print_all_messages() + return 1 + +# Processes HTTP request back to the browser. +class HTTPHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): + def send_head(self): + global page_last_served_time + path = self.translate_path(self.path) + f = None + + if os.path.isdir(path): + if not self.path.endswith('/'): + self.send_response(301) + self.send_header("Location", self.path + "/") + self.end_headers() + return None + for index in "index.html", "index.htm": + index = os.path.join(path, index) + if os.path.isfile(index): + path = index + break + else: + # Manually implement directory listing support. + return self.list_directory(path) + ctype = self.guess_type(path) + try: + f = open(path, 'rb') + except IOError: + self.send_error(404, "File not found: " + path) + return None + self.send_response(200) + self.send_header("Content-type", ctype) + fs = os.fstat(f.fileno()) + self.send_header("Content-Length", str(fs[6])) + self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) + self.send_header('Cache-Control','no-cache, must-revalidate') + self.send_header('Expires','-1') + self.end_headers() + page_last_served_time = time.clock() + return f + + def log_request(self, code): + # Filter out 200 OK messages to remove noise. + if code is not 200: + SimpleHTTPServer.SimpleHTTPRequestHandler.log_request(self, code) + + def log_message(self, format, *args): + msg = "%s - - [%s] %s\n" % (self.address_string(), self.log_date_time_string(), format%args) + if not 'favicon.ico' in msg: # Filter out 404 messages on favicon.ico not being found to remove noise. + sys.stderr.write(msg) + + def do_POST(self): + global page_exit_code, emrun_options, have_received_messages + + (_, _, path, query, _) = urlparse.urlsplit(self.path) + data = self.rfile.read(int(self.headers['Content-Length'])) + data = data.replace("+", " ") + data = unquote_u(data) + + # The user page sent a message with POST. Parse the message and log it to stdout/stderr. + is_stdout = False + is_stderr = False + seq_num = -1 + # The html shell is expected to send messages of form ^out^(number)^(message) or ^err^(number)^(message). + if data.startswith('^err^'): + is_stderr = True + elif data.startswith('^out^'): + is_stdout = True + if is_stderr or is_stdout: + try: + i = data.index('^', 5) + seq_num = int(data[5:i]) + data = data[i+1:] + except: + pass + + is_exit = data.startswith('^exit^') + + if data == '^pageload^': # Browser is just notifying that it has successfully launched the page. + have_received_messages = True + elif not is_exit: + log = browser_loge if is_stderr else browser_logi + self.server.handle_incoming_message(seq_num, log, data) + elif not emrun_options.serve_after_exit: + page_exit_code = int(data[6:]) + logv('Web page has quit with a call to exit() with return code ' + str(page_exit_code) + '. Shutting down web server. Pass --serve_after_exit to keep serving even after the page terminates with exit().') + self.server.shutdown() + + self.send_response(200) + self.send_header("Content-type", "text/plain") + self.send_header('Cache-Control','no-cache, must-revalidate') + self.send_header('Expires','-1') + self.end_headers() + self.wfile.write('OK') + +# From http://stackoverflow.com/questions/4842448/getting-processor-information-in-python +# Returns a string with something like "AMD64, Intel(R) Core(TM) i5-2557M CPU @ 1.70GHz, Intel64 Family 6 Model 42 Stepping 7, GenuineIntel" +def get_cpu_infoline(): + try: + if WINDOWS: + root_winmgmts = GetObject("winmgmts:root\cimv2") + cpus = root_winmgmts.ExecQuery("Select * from Win32_Processor") + cpu_name = cpus[0].Name + ', ' + platform.processor() + elif OSX: + cpu_name = subprocess.check_output(['sysctl', '-n', 'machdep.cpu.brand_string']).strip() + elif LINUX: + command = "cat /proc/cpuinfo" + all_info = subprocess.check_output(command, shell=True).strip() + for line in all_info.split("\n"): + if "model name" in line: + cpu_name = re.sub( ".*model name.*:", "", line,1).strip() + except: + return "Unknown" + + return platform.machine() + ', ' + cpu_name + +def get_android_cpu_infoline(): + lines = subprocess.check_output([ADB, 'shell', 'cat', '/proc/cpuinfo']).split('\n') + processor = '' + hardware = '' + for line in lines: + if line.startswith('Processor'): + processor = line[line.find(':')+1:].strip() + elif line.startswith('Hardware'): + hardware = line[line.find(':')+1:].strip() + + freq = int(subprocess.check_output([ADB, 'shell', 'cat', '/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq']).strip())/1000 + return 'CPU: ' + processor + ', ' + hardware + ' @ ' + str(freq) + ' MHz' + +def win_print_gpu_info(): + gpus = [] + gpu_memory = [] + + for i in range(0, 16): + try: + hHardwareReg = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, "HARDWARE") + hDeviceMapReg = _winreg.OpenKey(hHardwareReg, "DEVICEMAP") + hVideoReg = _winreg.OpenKey(hDeviceMapReg, "VIDEO") + VideoCardString = _winreg.QueryValueEx(hVideoReg,"\Device\Video"+str(i))[0] + #Get Rid of Registry/Machine from the string + VideoCardStringSplit = VideoCardString.split("\\") + ClearnVideoCardString = string.join(VideoCardStringSplit[3:], "\\") + #Go up one level for detailed + VideoCardStringRoot = string.join(VideoCardStringSplit[3:len(VideoCardStringSplit)-1], "\\") + + #Get the graphics card information + hVideoCardReg = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, ClearnVideoCardString) + try: + VideoCardDescription = _winreg.QueryValueEx(hVideoCardReg, "Device Description")[0] + except WindowsError: + VideoCardDescription = _winreg.QueryValueEx(hVideoCardReg, "DriverDesc")[0] + + try: + driverVersion = _winreg.QueryValueEx(hVideoCardReg, "DriverVersion")[0] + VideoCardDescription += ', driver version ' + driverVersion + except: + pass + + try: + driverDate = _winreg.QueryValueEx(hVideoCardReg, "DriverDate")[0] + VideoCardDescription += ' (' + driverDate + ')' + except: + pass + + VideoCardMemorySize = _winreg.QueryValueEx(hVideoCardReg, "HardwareInformation.MemorySize")[0] + try: + vram = struct.unpack('l',VideoCardMemorySize)[0] + except struct.error: + vram = int(VideoCardMemorySize) + if not VideoCardDescription in gpus: + gpus += [VideoCardDescription] + gpu_memory += [str(int(vram/1024/1024))] + except WindowsError: + pass + + if len(gpus) == 1: + print "GPU: " + gpus[0] + " with " + gpu_memory[0] + " MB of VRAM" + elif len(gpus) > 1: + for i in range(0, len(gpus)): + print "GPU"+str(i)+ ": " + gpus[i] + " with " + gpu_memory[i] + " MBs of VRAM" + +def linux_print_gpu_info(): + glxinfo = subprocess.check_output('glxinfo') + for line in glxinfo.split("\n"): + if "OpenGL vendor string:" in line: + gl_vendor = line[len("OpenGL vendor string:"):].strip() + if "OpenGL version string:" in line: + gl_version = line[len("OpenGL version string:"):].strip() + if "OpenGL renderer string:" in line: + gl_renderer = line[len("OpenGL renderer string:"):].strip() + logi('GPU: ' + gl_vendor + ' ' + gl_renderer + ', GL version ' + gl_version) + +def osx_print_gpu_info(): + try: + info = subprocess.check_output(['system_profiler', 'SPDisplaysDataType']) + gpu = re.search("Chipset Model: (.*)", info) + if gpu: + chipset = gpu.group(1) + vram = re.search("VRAM \(Total\): (.*) MB", info) + if vram: + logi('GPU: ' + chipset + ' with ' + vram.group(1) + ' MBs of VRAM') + else: + logi('GPU: ' + chipset) + except: + pass + +def print_gpu_infolines(): + if WINDOWS: + win_print_gpu_info() + elif LINUX: + linux_print_gpu_info() + elif OSX: + osx_print_gpu_info() + +def get_executable_version(filename): + try: + if WINDOWS: + info = GetFileVersionInfo(filename, "\\") + ms = info['FileVersionMS'] + ls = info['FileVersionLS'] + version = HIWORD (ms), LOWORD (ms), HIWORD (ls), LOWORD (ls) + return '.'.join(map(str, version)) + elif OSX: + plistfile = app_name[0:app_name.find('MacOS')] + 'Info.plist' + info = plistlib.readPlist(plistfile) + # Data in Info.plists is a bit odd, this check combo gives best information on each browser. + if 'firefox' in app_name.lower(): + return info['CFBundleShortVersionString'] + ' 20' + info['CFBundleVersion'][2:].replace('.', '-') + if 'opera' in app_name.lower(): + return info['CFBundleVersion'] + else: + return info['CFBundleShortVersionString'] + elif LINUX: + if 'firefox' in filename.lower(): + version = subprocess.check_output([filename, '-v']) + version = version.replace('Mozilla Firefox ', '') + return version.strip() + else: + return "" + except: + return "" + +# http://stackoverflow.com/questions/580924/python-windows-file-version-attribute +def win_get_file_properties(fname): + propNames = ('Comments', 'InternalName', 'ProductName', + 'CompanyName', 'LegalCopyright', 'ProductVersion', + 'FileDescription', 'LegalTrademarks', 'PrivateBuild', + 'FileVersion', 'OriginalFilename', 'SpecialBuild') + + props = {'FixedFileInfo': None, 'StringFileInfo': None, 'FileVersion': None} + + try: + # backslash as parm returns dictionary of numeric info corresponding to VS_FIXEDFILEINFO struc + fixedInfo = win32api.GetFileVersionInfo(fname, '\\') + props['FixedFileInfo'] = fixedInfo + props['FileVersion'] = "%d.%d.%d.%d" % (fixedInfo['FileVersionMS'] / 65536, + fixedInfo['FileVersionMS'] % 65536, fixedInfo['FileVersionLS'] / 65536, + fixedInfo['FileVersionLS'] % 65536) + + # \VarFileInfo\Translation returns list of available (language, codepage) + # pairs that can be used to retreive string info. We are using only the first pair. + lang, codepage = win32api.GetFileVersionInfo(fname, '\\VarFileInfo\\Translation')[0] + + # any other must be of the form \StringfileInfo\%04X%04X\parm_name, middle + # two are language/codepage pair returned from above + + strInfo = {} + for propName in propNames: + strInfoPath = u'\\StringFileInfo\\%04X%04X\\%s' % (lang, codepage, propName) + ## print str_info + strInfo[propName] = win32api.GetFileVersionInfo(fname, strInfoPath) + + props['StringFileInfo'] = strInfo + except: + pass + + return props + +def get_os_version(): + try: + if WINDOWS: + versionHandle = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion") + productName = _winreg.QueryValueEx(versionHandle, "ProductName") + return productName[0] + elif OSX: + return 'Mac OS ' + platform.mac_ver()[0] + elif LINUX: + kernel_version = subprocess.check_output(['uname', '-r']).strip() + return ' '.join(platform.linux_distribution()) + ', linux kernel ' + kernel_version + ' ' + platform.architecture()[0] + except: + return 'Unknown OS' + +def get_system_memory(): + global emrun_options + + try: + if LINUX or emrun_options.android: + if emrun_options.android: + lines = subprocess.check_output([ADB, 'shell', 'cat', '/proc/meminfo']).split('\n') + else: + mem = open('/proc/meminfo', 'r') + lines = mem.readlines() + mem.close() + for i in lines: + sline = i.split() + if str(sline[0]) == 'MemTotal:': + return int(sline[1]) * 1024 + elif WINDOWS: + return win32api.GlobalMemoryStatusEx()['TotalPhys'] + elif OSX: + return int(subprocess.check_output(['sysctl', '-n', 'hw.memsize']).strip()) + except: + return -1 + +# Finds the given executable 'program' in PATH. Operates like the Unix tool 'which'. +def which(program): + def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + path = path.strip('"') + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + + if WINDOWS and not '.' in fname: + if is_exe(exe_file + '.exe'): + return exe_file + '.exe' + if is_exe(exe_file + '.cmd'): + return exe_file + '.cmd' + if is_exe(exe_file + '.bat'): + return exe_file + '.bat' + + return None + +def win_get_default_browser(): + # Manually find the default system browser on Windows without relying on 'start %1' since + # that method has an issue, see comment below. + + # In Py3, this module is called winreg without the underscore + + with OpenKey(HKEY_CURRENT_USER, r"Software\Classes\http\shell\open\command") as key: + cmd = QueryValue(key, None) + if cmd: + parts = shlex.split(cmd) + if len(parts) > 0: + return [parts[0]] + + # Fall back to 'start %1', which we have to treat as if user passed --serve_forever, since + # for some reason, we are not able to detect when the browser closes when this is passed. + return ['cmd', '/C', 'start'] + +def find_browser(name): + if WINDOWS and name == 'start': + return win_get_default_browser() + if OSX and name == 'open': + return [name] + + if os.path.isfile(os.path.abspath(name)): + return [name] + if os.path.isfile(os.path.abspath(name)+'.exe'): + return [os.path.abspath(name)+'.exe'] + if os.path.isfile(os.path.abspath(name)+'.cmd'): + return [os.path.abspath(name)+'.cmd'] + if os.path.isfile(os.path.abspath(name)+'.bat'): + return [os.path.abspath(name)+'.bat'] + + path_lookup = which(name) + if path_lookup != None: + return [path_lookup] + + browser_locations = [] + if OSX: + # Note: by default Firefox beta installs as 'Firefox.app', you must manually rename it to + # FirefoxBeta.app after installation. + browser_locations = [ ('firefox', '/Applications/Firefox.app/Contents/MacOS/firefox'), + ('firefox_beta', '/Applications/FirefoxBeta.app/Contents/MacOS/firefox'), + ('firefox_aurora', '/Applications/FirefoxAurora.app/Contents/MacOS/firefox'), + ('firefox_nightly', '/Applications/FirefoxNightly.app/Contents/MacOS/firefox'), + ('safari', '/Applications/Safari.app/Contents/MacOS/Safari'), + ('opera', '/Applications/Opera.app/Contents/MacOS/Opera'), + ('chrome', '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'), + ('chrome_canary', '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary') ] + elif WINDOWS: + pf_locations = ['ProgramFiles(x86)', 'ProgramFiles', 'ProgramW6432'] + + for pf_env in pf_locations: + if not pf_env in os.environ: + continue + program_files = os.environ[pf_env] if WINDOWS else '' + + browser_locations += [ ('chrome', os.path.join(program_files, 'Google/Chrome/Application/chrome.exe')), + ('chrome_canary', os.path.expanduser("~/AppData/Local/Google/Chrome SxS/Application/chrome.exe")), + ('firefox_nightly', os.path.join(program_files, 'Nightly/firefox.exe')), + ('firefox_aurora', os.path.join(program_files, 'Aurora/firefox.exe')), + ('firefox_beta', os.path.join(program_files, 'Beta/firefox.exe')), + ('firefox_beta', os.path.join(program_files, 'FirefoxBeta/firefox.exe')), + ('firefox_beta', os.path.join(program_files, 'Firefox Beta/firefox.exe')), + ('firefox', os.path.join(program_files, 'Mozilla Firefox/firefox.exe')), + ('iexplore', os.path.join(program_files, 'Internet Explorer/iexplore.exe')), + ('opera', os.path.join(program_files, 'Opera/launcher.exe')) ] + + elif LINUX: + browser_locations = [ ('firefox', os.path.expanduser('~/firefox/firefox')), + ('firefox_beta', os.path.expanduser('~/firefox_beta/firefox')), + ('firefox_aurora', os.path.expanduser('~/firefox_aurora/firefox')), + ('firefox_nightly', os.path.expanduser('~/firefox_nightly/firefox')), + ('chrome', which('google-chrome-stable'))] + + for (alias, browser_exe) in browser_locations: + if name == alias: + if os.path.isfile(browser_exe): + return [browser_exe] + + return None # Could not find the browser + +def get_android_model(): + global ADB + manufacturer = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.manufacturer']).strip() + brand = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.brand']).strip() + model = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.model']).strip() + board = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.board']).strip() + device = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.device']).strip() + name = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.name']).strip() + return manufacturer + ' ' + brand + ' ' + model + ' ' + board + ' ' + device + ' ' + name + +def get_android_os_version(): + global ADB + ver = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.build.version.release']).strip() + apiLevel = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.build.version.sdk']).strip() + if not apiLevel: + apiLevel = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.build.version.sdk_int']).strip() + + os = '' + if ver: + os += 'Android ' + ver + ' ' + if apiLevel: + os += 'SDK API Level ' + apiLevel + ' ' + os += subprocess.check_output([ADB, 'shell', 'getprop', 'ro.build.description']).strip() + return os + +def list_android_browsers(): + global ADB + apps = subprocess.check_output([ADB, 'shell', 'pm', 'list', 'packages', '-f']).replace('\r\n', '\n') + browsers = [] + for line in apps.split('\n'): + line = line.strip() + if line.endswith('=org.mozilla.firefox'): + browsers += ['firefox'] + if line.endswith('=org.mozilla.firefox_beta'): + browsers += ['firefox_beta'] + if line.endswith('=org.mozilla.fennec_aurora'): + browsers += ['firefox_aurora'] + if line.endswith('=org.mozilla.fennec'): + browsers += ['firefox_nightly'] + if line.endswith('=com.android.chrome'): + browsers += ['chrome'] + if line.endswith('=com.chrome.beta'): + browsers += ['chrome_beta'] + if line.endswith('=com.opera.browser'): + browsers += ['opera'] + if line.endswith('=com.opera.mini.android'): + browsers += ['opera_mini'] + if line.endswith('=mobi.mgeek.TunnyBrowser'): + browsers += ['dolphin'] + + browsers.sort() + logi('emrun has automatically found the following browsers on the connected Android device:') + for browser in browsers: + logi(' - ' + browser) + +def list_pc_browsers(): + browsers = ['firefox', 'firefox_beta', 'firefox_aurora', 'firefox_nightly', 'chrome', 'chrome_canary', 'iexplore', 'safari', 'opera'] + logi('emrun has automatically found the following browsers in the default install locations on the system:') + logi('') + for browser in browsers: + browser_exe = find_browser(browser) + if type(browser_exe) == list: + browser_exe = browser_exe[0] + if browser_exe: + logi(' - ' + browser + ': ' + browser_display_name(browser_exe) + ' ' + get_executable_version(browser_exe)) + logi('') + logi('You can pass the --browser option to launch with the given browser above.') + logi('Even if your browser was not detected, you can use --browser /path/to/browser/executable to launch with that browser.') + +def browser_display_name(browser): + b = browser.lower() + if 'iexplore' in b: + return 'Microsoft Internet Explorer' + if 'chrome' in b: + return 'Google Chrome' + if 'firefox' in b: + # Try to identify firefox flavor explicitly, to help show issues where emrun would launch the wrong browser. + product_name = win_get_file_properties(browser)['StringFileInfo']['ProductName'] if WINDOWS else 'firefox' + if product_name.lower() != 'firefox': + return 'Mozilla Firefox ' + product_name + else: + return 'Mozilla Firefox' + if 'opera' in b: + return 'Opera' + if 'safari' in b: + return 'Apple Safari' + return browser + +def main(): + global browser_process, processname_killed_atexit, emrun_options, emrun_not_enabled_nag_printed, ADB + usage_str = "usage: %prog [options] [optional_portnum]" + parser = optparse.OptionParser(usage=usage_str) + + parser.add_option('--kill_start', dest='kill_on_start', action='store_true', default=False, + help='If true, any previously running instances of the target browser are killed before starting.') + + parser.add_option('--kill_exit', dest='kill_on_exit', action='store_true', default=False, + help='If true, the spawned browser process is forcibly killed when it calls exit().') + + parser.add_option('--no_server', dest='no_server', action='store_true', default=False, + help='If specified, a HTTP web server is not launched to host the page to run.') + + parser.add_option('--no_browser', dest='no_browser', action='store_true', default=False, + help='If specified, emrun will not launch a web browser to run the page.') + + parser.add_option('--no_emrun_detect', dest='no_emrun_detect', action='store_true', default=False, + help='If specified, skips printing the warning message if html page is detected to not have been built with --emrun linker flag.') + + parser.add_option('--serve_after_close', dest='serve_after_close', action='store_true', default=False, + help='If true, serves the web page even after the application quits by user closing the web page.') + + parser.add_option('--serve_after_exit', dest='serve_after_exit', action='store_true', default=False, + help='If true, serves the web page even after the application quits by a call to exit().') + + parser.add_option('--serve_root', dest='serve_root', default='', + help='If set, specifies the root path that the emrun web server serves. If not specified, the directory where the target .html page lives in is served.') + + parser.add_option('--verbose', dest='verbose', action='store_true', default=False, + help='Enable verbose logging from emrun internal operation.') + + parser.add_option('--port', dest='port', default=default_webserver_port, type="int", + help='Specifies the port the server runs in.') + + parser.add_option('--log_stdout', dest='log_stdout', default='', + help='Specifies a log filename where the browser process stdout data will be appended to.') + + parser.add_option('--log_stderr', dest='log_stderr', default='', + help='Specifies a log filename where the browser process stderr data will be appended to.') + + parser.add_option('--silence_timeout', dest='silence_timeout', type="int", default=0, + help='If no activity is received in this many seconds, the browser process is assumed to be hung, and the web server is shut down and the target browser killed. Disabled by default.') + + parser.add_option('--timeout', dest='timeout', type="int", default=0, + help='If the browser process does not quit or the page exit() in this many seconds, the browser is assumed to be hung, and the web server is shut down and the target browser killed. Disabled by default.') + + parser.add_option('--timeout_returncode', dest='timeout_returncode', type="int", default=99999, + help='Sets the exit code that emrun reports back to caller in the case that a page timeout occurs. Default: 99999.') + + parser.add_option('--list_browsers', dest='list_browsers', action='store_true', + help='Prints out all detected browser that emrun is able to use with the --browser command and exits.') + + parser.add_option('--browser', dest='browser', default='', + help='Specifies the browser executable to run the web page in.') + + parser.add_option('--android', dest='android', action='store_true', default=False, + help='Launches the page in a browser of an Android device connected to an USB on the local system. (via adb)') + + parser.add_option('--system_info', dest='system_info', action='store_true', + help='Prints information about the current system at startup.') + + parser.add_option('--browser_info', dest='browser_info', action='store_true', + help='Prints information about the target browser to launch at startup.') + + parser.add_option('--log_html', dest='log_html', action='store_true', + help='If set, information lines are printed out an HTML-friendly format.') + + (options, args) = parser.parse_args(sys.argv) + emrun_options = options + + if options.android: + ADB = which('adb') + if not ADB: + loge("Could not find the adb tool. Install Android SDK and add the directory of adb to PATH.") + return 1 + + if not options.browser and not options.android: + if WINDOWS: + options.browser = 'start' + elif LINUX: + options.browser = which('xdg-open') + if not options.browser: + options.browser = 'firefox' + elif OSX: + options.browser = 'safari' + + if options.list_browsers: + if options.android: + list_android_browsers() + else: + list_pc_browsers() + return + + if len(args) < 2 and (options.system_info or options.browser_info): + options.no_server = options.no_browser = True # Don't run if only --system_info or --browser_info was passed. + + if len(args) < 2 and not (options.no_server == True and options.no_browser == True): + logi('Usage: emrun filename.html') + return + + file_to_serve = args[1] if len(args) > 1 else '' + cmdlineparams = args[2:] if len(args) > 2 else [] + + if options.serve_root: + serve_dir = os.path.abspath(options.serve_root) + else: + serve_dir = os.path.dirname(os.path.abspath(file_to_serve)) + url = os.path.relpath(os.path.abspath(file_to_serve), serve_dir) + if len(cmdlineparams) > 0: + url += '?' + '&'.join(cmdlineparams) + server_root = 'localhost' + if options.android: + server_root = socket.gethostbyname(socket.gethostname()) + url = 'http://' + server_root + ':' + str(options.port)+'/'+url + + os.chdir(serve_dir) + logv('Web server root directory: ' + os.path.abspath('.')) + + if options.android: + if not options.no_browser: + if not options.browser: + loge("Running on Android requires that you explicitly specify the browser to run with --browser . Run emrun --android --list_browsers to obtain a list of installed browsers you can use.") + return 1 + elif options.browser == 'firefox': + browser_app = 'org.mozilla.firefox/.App' + elif options.browser == 'firefox_beta': + browser_app = 'org.mozilla.firefox_beta/.App' + elif options.browser == 'firefox_aurora' or options.browser == 'fennec_aurora': + browser_app = 'org.mozilla.fennec_aurora/.App' + elif options.browser == 'firefox_nightly' or options.browser == 'fennec': + browser_app = 'org.mozilla.fennec/.App' + elif options.browser == 'chrome': + browser_app = 'com.android.chrome/.Main' + elif options.browser == 'chrome_beta' or options.browser == 'chrome_canary': # There is no Chrome Canary for Android, but Play store has 'Chrome Beta' instead. + browser_app = 'com.chrome.beta/com.android.chrome.Main' + elif options.browser == 'opera': + browser_app = 'com.opera.browser/com.opera.Opera' + elif options.browser == 'opera_mini': # Launching the URL works, but page seems to never load (Fails with 'Network problem' even when other browsers work) + browser_app = 'com.opera.mini.android/.Browser' + elif options.browser =='dolphin': # Current stable Dolphin as of 12/2013 does not have WebGL support. + browser_app = 'mobi.mgeek.TunnyBrowser/.BrowserActivity' + else: + loge("Don't know how to launch browser " + options.browser + ' on Android!') + return 1 + # To add support for a new Android browser in the list above: + # 1. Install the browser to Android phone, connect it via adb to PC. + # 2. Type 'adb shell pm list packages -f' to locate the package name of that application. + # 3. Type 'adb pull .apk' to copy the apk of that application to PC. + # 4. Type 'aapt d xmltree .apk AndroidManifest.xml > manifest.txt' to extract the manifest from the package. + # 5. Locate the name of the main activity for the browser in manifest.txt and add an entry to above list in form 'appname/mainactivityname' + + if WINDOWS: + url = url.replace('&', '\\&') + browser = [ADB, 'shell', 'am', 'start', '-a', 'android.intent.action.VIEW', '-n', browser_app, '-d', url] + processname_killed_atexit = browser_app[:browser_app.find('/')] + else: #Launching a web page on local system. + browser = find_browser(str(options.browser)) + browser_exe = browser[0] + browser_args = [] + + if 'safari' in browser_exe.lower(): + # Safari has a bug that a command line 'Safari http://page.com' does not launch that page, + # but instead launches 'file:///http://page.com'. To remedy this, must use the open -a command + # to run Safari, but unfortunately this will end up spawning Safari process detached from emrun. + if OSX: + browser = ['open', '-a', 'Safari'] + (browser[1:] if len(browser) > 1 else []) + + processname_killed_atexit = 'Safari' + elif 'chrome' in browser_exe.lower(): + processname_killed_atexit = 'chrome' + browser_args = ['--incognito', '--enable-nacl', '--enable-pnacl', '--disable-restore-session-state', '--enable-webgl', '--no-default-browser-check', '--no-first-run', '--allow-file-access-from-files'] + # if options.no_server: + # browser_args += ['--disable-web-security'] + elif 'firefox' in browser_exe.lower(): + processname_killed_atexit = 'firefox' + elif 'iexplore' in browser_exe.lower(): + processname_killed_atexit = 'iexplore' + browser_args = ['-private'] + elif 'opera' in browser_exe.lower(): + processname_killed_atexit = 'opera' + + # In Windows cmdline, & character delimits multiple commmands, so must use ^ to escape them. + if browser_exe == 'cmd': + url = url.replace('&', '^&') + browser += browser_args + [url] + + if options.kill_on_start: + pname = processname_killed_atexit + kill_browser_process() + processname_killed_atexit = pname + + if options.system_info: + logi('Time of run: ' + time.strftime("%x %X")) + if options.android: + logi('Model: ' + get_android_model()) + logi('OS: ' + get_android_os_version() + ' with ' + str(get_system_memory()/1024/1024) + ' MB of System RAM') + logi('CPU: ' + get_android_cpu_infoline()) + else: + logi('Computer name: ' + socket.gethostname()) # http://stackoverflow.com/questions/799767/getting-name-of-windows-computer-running-python-script + logi('OS: ' + get_os_version() + ' with ' + str(get_system_memory()/1024/1024) + ' MB of System RAM') + logi('CPU: ' + get_cpu_infoline()) + print_gpu_infolines() + if options.browser_info: + if options.android: + logi('Browser: Android ' + browser_app) + else: + logi('Browser: ' + browser_display_name(browser[0]) + ' ' + get_executable_version(browser_exe)) + + # Suppress run warning if requested. + if options.no_emrun_detect: + emrun_not_enabled_nag_printed = True + + global browser_stdout_handle, browser_stderr_handle + if options.log_stdout: + browser_stdout_handle = open(options.log_stdout, 'ab') + if options.log_stderr: + if options.log_stderr == options.log_stdout: + browser_stderr_handle = browser_stdout_handle + else: + browser_stderr_handle = open(options.log_stderr, 'ab') + + if not options.no_server: + logv('Starting web server in port ' + str(options.port)) + httpd = HTTPWebServer(('', options.port), HTTPHandler) + + if not options.no_browser: + logv("Executing %s" % ' '.join(browser)) + if browser[0] == 'cmd': + serve_forever = True # Workaround an issue where passing 'cmd /C start' is not able to detect when the user closes the page. + browser_process = subprocess.Popen(browser) + if options.kill_on_exit: + atexit.register(kill_browser_process) + # For Android automation, we execute adb, so this process does not represent a browser and no point killing it. + if options.android: + browser_process = None + + if browser_process and browser_process.poll() == None: + options.serve_after_close = True + logv('Warning: emrun got detached from the target browser process. Cannot detect when user closes the browser. Behaving as if --serve_after_close was passed in.') + + if not options.no_server: + try: + httpd.serve_forever() + except KeyboardInterrupt: + httpd.server_close() + httpd.server_close() + + logv('Closed web server.') + + if not options.no_browser: + if options.kill_on_exit: + kill_browser_process() + elif is_browser_process_alive(): + logv('Not terminating browser process, pass --kill_exit to terminate the browser when it calls exit().') + + return page_exit_code + +if __name__ == '__main__': + returncode = main() + logv('emrun quitting with process exit code ' + str(returncode)) + sys.exit(returncode) diff --git a/emrun.bat b/emrun.bat new file mode 100644 index 0000000000000..ae937e4daa650 --- /dev/null +++ b/emrun.bat @@ -0,0 +1,2 @@ +@echo off +python "%~dp0\emrun" %* \ No newline at end of file diff --git a/emscripten.py b/emscripten.py index 75e6711a39965..42db080352888 100755 --- a/emscripten.py +++ b/emscripten.py @@ -9,7 +9,7 @@ headers, for the libc implementation in JS). ''' -import os, sys, json, optparse, subprocess, re, time, multiprocessing, string, logging +import os, sys, json, optparse, subprocess, re, time, multiprocessing, string, logging, shutil from tools import shared from tools import jsrun, cache as cache_module, tempfiles @@ -706,6 +706,499 @@ def fix(m): outfile.close() +# emscript_fast: emscript'en code using the 'fast' compilation path, using +# an LLVM backend +# FIXME: this is just a copy-paste of normal emscript(), and we trample it +# if the proper env var is set (see below). we should refactor to +# share code between the two, once emscript_fast stabilizes (or, +# leaving it separate like it is will make it trivial to rip out +# if the experiment fails) + +def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None, + jcache=None, temp_files=None, DEBUG=None, DEBUG_CACHE=None): + """Runs the emscripten LLVM-to-JS compiler. We parallelize as much as possible + + Args: + infile: The path to the input LLVM assembly file. + settings: JSON-formatted settings that override the values + defined in src/settings.js. + outfile: The file where the output is written. + """ + + assert(settings['ASM_JS']) # TODO: apply ASM_JS even in -O0 for fastcomp + + # Overview: + # * Run LLVM backend to emit JS. JS includes function bodies, memory initializer, + # and various metadata + # * Run compiler.js on the metadata to emit the shell js code, pre/post-ambles, + # JS library dependencies, etc. + + if DEBUG: + logging.debug('emscript: llvm backend') + t = time.time() + + temp_js = temp_files.get('.4.js').name + backend_compiler = os.path.join(shared.LLVM_ROOT, 'llc') + shared.jsrun.timeout_run(subprocess.Popen([backend_compiler, infile, '-march=js', '-filetype=asm', '-o', temp_js], stdout=subprocess.PIPE)) + + if DEBUG: + logging.debug(' emscript: llvm backend took %s seconds' % (time.time() - t)) + t = time.time() + + # Split up output + backend_output = open(temp_js).read() + #if DEBUG: print >> sys.stderr, backend_output + + start_funcs_marker = '// EMSCRIPTEN_START_FUNCTIONS' + end_funcs_marker = '// EMSCRIPTEN_END_FUNCTIONS' + metadata_split_marker = '// EMSCRIPTEN_METADATA' + + start_funcs = backend_output.index(start_funcs_marker) + end_funcs = backend_output.rindex(end_funcs_marker) + metadata_split = backend_output.rindex(metadata_split_marker) + + funcs = backend_output[start_funcs+len(start_funcs_marker):end_funcs] + metadata_raw = backend_output[metadata_split+len(metadata_split_marker):] + #if DEBUG: print >> sys.stderr, "METAraw", metadata_raw + metadata = json.loads(metadata_raw) + mem_init = backend_output[end_funcs+len(end_funcs_marker):metadata_split] + #if DEBUG: print >> sys.stderr, "FUNCS", funcs + #if DEBUG: print >> sys.stderr, "META", metadata + #if DEBUG: print >> sys.stderr, "meminit", mem_init + + # function table masks + + table_sizes = {} + for k, v in metadata['tables'].iteritems(): + table_sizes[k] = str(v.count(',')) # undercounts by one, but that is what we want + funcs = re.sub(r"#FM_(\w+)#", lambda m: table_sizes[m.groups(0)[0]], funcs) + + # fix +float into float.0, if not running js opts + if not settings['RUNNING_JS_OPTS']: + def fix_dot_zero(m): + num = m.group(3) + # TODO: handle 0x floats? + if num.find('.') < 0: + e = num.find('e'); + if e < 0: + num += '.0' + else: + num = num[:e] + '.0' + num[e:] + return m.group(1) + m.group(2) + num + funcs = re.sub(r'([(=,+\-*/%<>:?] *)\+(-?)((0x)?[0-9a-f]*\.?[0-9]+([eE][-+]?[0-9]+)?)', lambda m: fix_dot_zero(m), funcs) + + # js compiler + + if DEBUG: logging.debug('emscript: js compiler glue') + + # Settings changes + assert settings['TARGET_LE32'] == 1 + settings['TARGET_LE32'] = 2 + if 'i64Add' in metadata['declares']: # TODO: others, once we split them up + settings['PRECISE_I64_MATH'] = 2 + metadata['declares'] = filter(lambda i64_func: i64_func not in ['getHigh32', 'setHigh32', '__muldi3', '__divdi3', '__remdi3', '__udivdi3', '__uremdi3'], metadata['declares']) # FIXME: do these one by one as normal js lib funcs + + # Integrate info from backend + settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE'] = list( + set(settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE'] + map(shared.JS.to_nice_ident, metadata['declares'])).difference( + map(lambda x: x[1:], metadata['implementedFunctions']) + ) + ) + map(lambda x: x[1:], metadata['externs']) + + # Save settings to a file to work around v8 issue 1579 + settings_file = temp_files.get('.txt').name + def save_settings(): + global settings_text + settings_text = json.dumps(settings, sort_keys=True) + s = open(settings_file, 'w') + s.write(settings_text) + s.close() + save_settings() + + # Call js compiler + if DEBUG: t = time.time() + out = jsrun.run_js(path_from_root('src', 'compiler.js'), compiler_engine, [settings_file, ';', 'glue'] + libraries, stdout=subprocess.PIPE, stderr=STDERR_FILE, + cwd=path_from_root('src')) + assert '//FORWARDED_DATA:' in out, 'Did not receive forwarded data in pre output - process failed?' + glue, forwarded_data = out.split('//FORWARDED_DATA:') + + if DEBUG: + logging.debug(' emscript: glue took %s seconds' % (time.time() - t)) + t = time.time() + + last_forwarded_json = forwarded_json = json.loads(forwarded_data) + + # merge in information from llvm backend + + last_forwarded_json['Functions']['tables'] = metadata['tables'] + + '''indexed_functions = set() + for key in forwarded_json['Functions']['indexedFunctions'].iterkeys(): + indexed_functions.add(key)''' + + pre, post = glue.split('// EMSCRIPTEN_END_FUNCS') + + #print >> sys.stderr, 'glue:', pre, '\n\n||||||||||||||||\n\n', post, '...............' + + # memory and global initializers + + global_initializers = ', '.join(map(lambda i: '{ func: function() { %s() } }' % i, metadata['initializers'])) + + pre = pre.replace('STATICTOP = STATIC_BASE + 0;', '''STATICTOP = STATIC_BASE + Runtime.alignMemory(%d); +/* global initializers */ __ATINIT__.push(%s); +%s''' % (mem_init.count(',')+1, global_initializers, mem_init)) # XXX wrong size calculation! + + funcs_js = [funcs] + if settings.get('ASM_JS'): + parts = pre.split('// ASM_LIBRARY FUNCTIONS\n') + if len(parts) > 1: + pre = parts[0] + funcs_js.append(parts[1]) + + # calculations on merged forwarded data TODO + + # merge forwarded data + assert settings.get('ASM_JS'), 'fastcomp is asm.js only' + settings['EXPORTED_FUNCTIONS'] = forwarded_json['EXPORTED_FUNCTIONS'] + all_exported_functions = set(settings['EXPORTED_FUNCTIONS']) # both asm.js and otherwise + for additional_export in settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE']: # additional functions to export from asm, if they are implemented + all_exported_functions.add('_' + additional_export) + exported_implemented_functions = set() + export_bindings = settings['EXPORT_BINDINGS'] + export_all = settings['EXPORT_ALL'] + for key in metadata['implementedFunctions'] + forwarded_json['Functions']['implementedFunctions'].keys(): # XXX perf + if key in all_exported_functions or export_all or (export_bindings and key.startswith('_emscripten_bind')): + exported_implemented_functions.add(key) + + #if DEBUG: outfile.write('// pre\n') + outfile.write(pre) + pre = None + + #if DEBUG: outfile.write('// funcs\n') + + if settings.get('ASM_JS'): + # Move preAsms to their right place + def move_preasm(m): + contents = m.groups(0)[0] + outfile.write(contents + '\n') + return '' + funcs_js[1] = re.sub(r'/\* PRE_ASM \*/(.*)\n', lambda m: move_preasm(m), funcs_js[1]) + + funcs_js += ['\n// EMSCRIPTEN_END_FUNCS\n'] + + simple = os.environ.get('EMCC_SIMPLE_ASM') + class Counter: + i = 0 + j = 0 + if 'pre' in last_forwarded_json['Functions']['tables']: + pre_tables = last_forwarded_json['Functions']['tables']['pre'] + del last_forwarded_json['Functions']['tables']['pre'] + else: + pre_tables = '' + + def make_table(sig, raw): + i = Counter.i + Counter.i += 1 + bad = 'b' + str(i) + params = ','.join(['p%d' % p for p in range(len(sig)-1)]) + coerced_params = ','.join([shared.JS.make_coercion('p%d', sig[p+1], settings) % p for p in range(len(sig)-1)]) + coercions = ';'.join(['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, sig[p+1], settings)) for p in range(len(sig)-1)]) + ';' + def make_func(name, code): + return 'function %s(%s) { %s %s }' % (name, params, coercions, code) + Counter.pre = [make_func(bad, ('abort' if not settings['ASSERTIONS'] else 'nullFunc') + '(' + str(i) + ');' + ( + '' if sig[0] == 'v' else ('return %s' % shared.JS.make_initializer(sig[0], settings)) + ))] + start = raw.index('[') + end = raw.rindex(']') + body = raw[start+1:end].split(',') + for j in range(settings['RESERVED_FUNCTION_POINTERS']): + body[settings['FUNCTION_POINTER_ALIGNMENT'] * (1 + j)] = 'jsCall_%s_%s' % (sig, j) + Counter.j = 0 + def fix_item(item): + Counter.j += 1 + newline = Counter.j % 30 == 29 + if item == '0': return bad if not newline else (bad + '\n') + if item not in metadata['implementedFunctions']: + # this is imported into asm, we must wrap it + call_ident = item + if call_ident in metadata['redirects']: call_ident = metadata['redirects'][call_ident] + if not call_ident.startswith('_') and not call_ident.startswith('Math_'): call_ident = '_' + call_ident + code = call_ident + '(' + coerced_params + ')' + if sig[0] != 'v': + code = 'return ' + shared.JS.make_coercion(code, sig[0], settings) + code += ';' + Counter.pre.append(make_func(item + '__wrapper', code)) + return item + '__wrapper' + return item if not newline else (item + '\n') + body = ','.join(map(fix_item, body)) + return ('\n'.join(Counter.pre), ''.join([raw[:start+1], body, raw[end:]])) + + infos = [make_table(sig, raw) for sig, raw in last_forwarded_json['Functions']['tables'].iteritems()] + Counter.pre = [] + + function_tables_defs = '\n'.join([info[0] for info in infos]) + '\n// EMSCRIPTEN_END_FUNCS\n' + '\n'.join([info[1] for info in infos]) + + asm_setup = '' + maths = ['Math.' + func for func in ['floor', 'abs', 'sqrt', 'pow', 'cos', 'sin', 'tan', 'acos', 'asin', 'atan', 'atan2', 'exp', 'log', 'ceil', 'imul']] + fundamentals = ['Math', 'Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Float32Array', 'Float64Array'] + math_envs = ['Math.min'] # TODO: move min to maths + asm_setup += '\n'.join(['var %s = %s;' % (f.replace('.', '_'), f) for f in math_envs]) + + if settings['PRECISE_F32']: maths += ['Math.fround'] + + basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs] + if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall') + if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE', 'SAFE_HEAP_CLEAR'] + if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8'] + if settings['ASSERTIONS']: + basic_funcs += ['nullFunc'] + asm_setup += 'function nullFunc(x) { Module["printErr"]("Invalid function pointer called. Perhaps a miscast function pointer (check compilation warnings) or bad vtable lookup (maybe due to derefing a bad pointer, like NULL)?"); abort(x) }\n' + + basic_vars = ['STACKTOP', 'STACK_MAX', 'tempDoublePtr', 'ABORT'] + basic_float_vars = ['NaN', 'Infinity'] + + if metadata.get('preciseI64MathUsed') or \ + forwarded_json['Functions']['libraryFunctions'].get('llvm_cttz_i32') or \ + forwarded_json['Functions']['libraryFunctions'].get('llvm_ctlz_i32'): + basic_vars += ['cttz_i8', 'ctlz_i8'] + + if settings.get('DLOPEN_SUPPORT'): + for sig in last_forwarded_json['Functions']['tables'].iterkeys(): + basic_vars.append('F_BASE_%s' % sig) + asm_setup += ' var F_BASE_%s = %s;\n' % (sig, 'FUNCTION_TABLE_OFFSET' if settings.get('SIDE_MODULE') else '0') + '\n' + + asm_runtime_funcs = ['stackAlloc', 'stackSave', 'stackRestore', 'setThrew'] + ['setTempRet%d' % i for i in range(10)] + # function tables + function_tables = ['dynCall_' + table for table in last_forwarded_json['Functions']['tables']] + function_tables_impls = [] + + for sig in last_forwarded_json['Functions']['tables'].iterkeys(): + args = ','.join(['a' + str(i) for i in range(1, len(sig))]) + arg_coercions = ' '.join(['a' + str(i) + '=' + shared.JS.make_coercion('a' + str(i), sig[i], settings) + ';' for i in range(1, len(sig))]) + coerced_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings) for i in range(1, len(sig))]) + ret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('FUNCTION_TABLE_%s[index&{{{ FTM_%s }}}](%s)' % (sig, sig, coerced_args), sig[0], settings) + function_tables_impls.append(''' + function dynCall_%s(index%s%s) { + index = index|0; + %s + %s; + } +''' % (sig, ',' if len(sig) > 1 else '', args, arg_coercions, ret)) + + for i in range(settings['RESERVED_FUNCTION_POINTERS']): + jsret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('jsCall(%d%s%s)' % (i, ',' if coerced_args else '', coerced_args), sig[0], settings) + function_tables_impls.append(''' + function jsCall_%s_%s(%s) { + %s + %s; + } + +''' % (sig, i, args, arg_coercions, jsret)) + shared.Settings.copy(settings) + asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n' + basic_funcs.append('invoke_%s' % sig) + if settings.get('DLOPEN_SUPPORT'): + asm_setup += '\n' + shared.JS.make_extcall(sig) + '\n' + basic_funcs.append('extCall_%s' % sig) + + # calculate exports + exported_implemented_functions = list(exported_implemented_functions) + metadata['initializers'] + exported_implemented_functions.append('runPostSets') + exports = [] + if not simple: + for export in exported_implemented_functions + asm_runtime_funcs + function_tables: + exports.append("%s: %s" % (export, export)) + exports = '{ ' + ', '.join(exports) + ' }' + else: + exports = '_main' + # calculate globals + try: + del forwarded_json['Variables']['globals']['_llvm_global_ctors'] # not a true variable + except: + pass + # If no named globals, only need externals + global_vars = metadata['externs'] #+ forwarded_json['Variables']['globals'] + global_funcs = list(set(['_' + key for key, value in forwarded_json['Functions']['libraryFunctions'].iteritems() if value != 2]).difference(set(global_vars)).difference(set(metadata['implementedFunctions']))) + def math_fix(g): + return g if not g.startswith('Math_') else g.split('_')[1] + asm_global_funcs = ''.join([' var ' + g.replace('.', '_') + '=global.' + g + ';\n' for g in maths]) + \ + ''.join([' var ' + g + '=env.' + math_fix(g) + ';\n' for g in basic_funcs + global_funcs]) + asm_global_vars = ''.join([' var ' + g + '=env.' + g + '|0;\n' for g in basic_vars + global_vars]) + # In linkable modules, we need to add some explicit globals for global variables that can be linked and used across modules + if settings.get('MAIN_MODULE') or settings.get('SIDE_MODULE'): + assert settings.get('TARGET_LE32'), 'TODO: support x86 target when linking modules (needs offset of 4 and not 8 here)' + for key, value in forwarded_json['Variables']['globals'].iteritems(): + if value.get('linkable'): + init = forwarded_json['Variables']['indexedGlobals'][key] + 8 # 8 is Runtime.GLOBAL_BASE / STATIC_BASE + if settings.get('SIDE_MODULE'): init = '(H_BASE+' + str(init) + ')|0' + asm_global_vars += ' var %s=%s;\n' % (key, str(init)) + + # sent data + the_global = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in fundamentals]) + ' }' + sending = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in basic_funcs + global_funcs + basic_vars + basic_float_vars + global_vars]) + ' }' + # received + if not simple: + receiving = ';\n'.join(['var ' + s + ' = Module["' + s + '"] = asm["' + s + '"]' for s in exported_implemented_functions + function_tables]) + else: + receiving = 'var _main = Module["_main"] = asm;' + + # finalize + + if DEBUG: logging.debug('asm text sizes' + str([map(len, funcs_js), len(asm_setup), len(asm_global_vars), len(asm_global_funcs), len(pre_tables), len('\n'.join(function_tables_impls)), len(function_tables_defs.replace('\n', '\n ')), len(exports), len(the_global), len(sending), len(receiving)])) + + funcs_js = [''' +%s +function asmPrintInt(x, y) { + Module.print('int ' + x + ',' + y);// + ' ' + new Error().stack); +} +function asmPrintFloat(x, y) { + Module.print('float ' + x + ',' + y);// + ' ' + new Error().stack); +} +// EMSCRIPTEN_START_ASM +var asm = (function(global, env, buffer) { + %s + var HEAP8 = new global.Int8Array(buffer); + var HEAP16 = new global.Int16Array(buffer); + var HEAP32 = new global.Int32Array(buffer); + var HEAPU8 = new global.Uint8Array(buffer); + var HEAPU16 = new global.Uint16Array(buffer); + var HEAPU32 = new global.Uint32Array(buffer); + var HEAPF32 = new global.Float32Array(buffer); + var HEAPF64 = new global.Float64Array(buffer); +''' % (asm_setup, "'use asm';" if not metadata.get('hasInlineJS') and not settings['SIDE_MODULE'] and settings['ASM_JS'] == 1 else "'almost asm';") + '\n' + asm_global_vars + ''' + var __THREW__ = 0; + var threwValue = 0; + var setjmpId = 0; + var undef = 0; + var nan = +env.NaN, inf = +env.Infinity; + var tempInt = 0, tempBigInt = 0, tempBigIntP = 0, tempBigIntS = 0, tempBigIntR = 0.0, tempBigIntI = 0, tempBigIntD = 0, tempValue = 0, tempDouble = 0.0; +''' + ''.join([''' + var tempRet%d = 0;''' % i for i in range(10)]) + '\n' + asm_global_funcs] + [' var tempFloat = %s;\n' % ('Math_fround(0)' if settings.get('PRECISE_F32') else '0.0')] + [''' +// EMSCRIPTEN_START_FUNCS +function stackAlloc(size) { + size = size|0; + var ret = 0; + ret = STACKTOP; + STACKTOP = (STACKTOP + size)|0; +''' + ('STACKTOP = (STACKTOP + 3)&-4;' if settings['TARGET_X86'] else 'STACKTOP = (STACKTOP + 7)&-8;') + ''' + return ret|0; +} +function stackSave() { + return STACKTOP|0; +} +function stackRestore(top) { + top = top|0; + STACKTOP = top; +} +function setThrew(threw, value) { + threw = threw|0; + value = value|0; + if ((__THREW__|0) == 0) { + __THREW__ = threw; + threwValue = value; + } +} +function copyTempFloat(ptr) { + ptr = ptr|0; + HEAP8[tempDoublePtr] = HEAP8[ptr]; + HEAP8[tempDoublePtr+1|0] = HEAP8[ptr+1|0]; + HEAP8[tempDoublePtr+2|0] = HEAP8[ptr+2|0]; + HEAP8[tempDoublePtr+3|0] = HEAP8[ptr+3|0]; +} +function copyTempDouble(ptr) { + ptr = ptr|0; + HEAP8[tempDoublePtr] = HEAP8[ptr]; + HEAP8[tempDoublePtr+1|0] = HEAP8[ptr+1|0]; + HEAP8[tempDoublePtr+2|0] = HEAP8[ptr+2|0]; + HEAP8[tempDoublePtr+3|0] = HEAP8[ptr+3|0]; + HEAP8[tempDoublePtr+4|0] = HEAP8[ptr+4|0]; + HEAP8[tempDoublePtr+5|0] = HEAP8[ptr+5|0]; + HEAP8[tempDoublePtr+6|0] = HEAP8[ptr+6|0]; + HEAP8[tempDoublePtr+7|0] = HEAP8[ptr+7|0]; +} +''' + ''.join([''' +function setTempRet%d(value) { + value = value|0; + tempRet%d = value; +} +''' % (i, i) for i in range(10)])] + funcs_js + [''' + %s + + return %s; +}) +// EMSCRIPTEN_END_ASM +(%s, %s, buffer); +%s; +''' % (pre_tables + '\n'.join(function_tables_impls) + '\n' + function_tables_defs.replace('\n', '\n '), exports, the_global, sending, receiving)] + + if not settings.get('SIDE_MODULE'): + funcs_js.append(''' +Runtime.stackAlloc = function(size) { return asm['stackAlloc'](size) }; +Runtime.stackSave = function() { return asm['stackSave']() }; +Runtime.stackRestore = function(top) { asm['stackRestore'](top) }; +''') + + # Set function table masks + masks = {} + max_mask = 0 + for sig, table in last_forwarded_json['Functions']['tables'].iteritems(): + mask = table.count(',') + masks[sig] = str(mask) + max_mask = max(mask, max_mask) + def function_table_maskize(js, masks): + def fix(m): + sig = m.groups(0)[0] + return masks[sig] + return re.sub(r'{{{ FTM_([\w\d_$]+) }}}', lambda m: fix(m), js) # masks[m.groups(0)[0]] + funcs_js = map(lambda js: function_table_maskize(js, masks), funcs_js) + + if settings.get('DLOPEN_SUPPORT'): + funcs_js.append(''' + asm.maxFunctionIndex = %(max_mask)d; + DLFCN.registerFunctions(asm, %(max_mask)d+1, %(sigs)s, Module); + Module.SYMBOL_TABLE = SYMBOL_TABLE; +''' % { 'max_mask': max_mask, 'sigs': str(map(str, last_forwarded_json['Functions']['tables'].keys())) }) + + else: + function_tables_defs = '\n'.join([table for table in last_forwarded_json['Functions']['tables'].itervalues()]) + outfile.write(function_tables_defs) + funcs_js = [''' +// EMSCRIPTEN_START_FUNCS +'''] + funcs_js + [''' +// EMSCRIPTEN_END_FUNCS +'''] + + # Create symbol table for self-dlopen + if settings.get('DLOPEN_SUPPORT'): + symbol_table = {} + for k, v in forwarded_json['Variables']['indexedGlobals'].iteritems(): + if forwarded_json['Variables']['globals'][k]['named']: + symbol_table[k] = str(v + forwarded_json['Runtime']['GLOBAL_BASE']) + for raw in last_forwarded_json['Functions']['tables'].itervalues(): + if raw == '': continue + table = map(string.strip, raw[raw.find('[')+1:raw.find(']')].split(",")) + for i in range(len(table)): + value = table[i] + if value != '0': + if settings.get('SIDE_MODULE'): + symbol_table[value] = 'FUNCTION_TABLE_OFFSET+' + str(i) + else: + symbol_table[value] = str(i) + outfile.write("var SYMBOL_TABLE = %s;" % json.dumps(symbol_table).replace('"', '')) + + for i in range(len(funcs_js)): # do this loop carefully to save memory + outfile.write(funcs_js[i]) + funcs_js = None + + outfile.write(post) + + outfile.close() + + if DEBUG: logging.debug(' emscript: final python processing took %s seconds' % (time.time() - t)) + +if os.environ.get('EMCC_FAST_COMPILER'): + emscript = emscript_fast + def main(args, compiler_engine, cache, jcache, relooper, temp_files, DEBUG, DEBUG_CACHE): # Prepare settings for serialization to JSON. settings = {} diff --git a/src/analyzer.js b/src/analyzer.js index 253c5505f4e32..e8ca6cf6fc96c 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -1570,7 +1570,17 @@ function analyzer(data, sidePass) { for (var j = 0; j < label.lines.length; j++) { var line = label.lines[j]; if ((line.intertype == 'call' || line.intertype == 'invoke') && line.ident == setjmp) { - // Add a new label + if (line.intertype == 'invoke') { + // setjmp cannot trigger unwinding, so just reduce the invoke to a call + branch + line.intertype = 'call'; + label.lines.push({ + intertype: 'branch', + label: line.toLabel, + lineNum: line.lineNum + 0.01, // XXX legalizing might confuse this + }); + line.toLabel = line.unwindLabel = -2; + } + // split this label into up to the setjmp (including), then a new label for the rest. longjmp will reach the rest var oldLabel = label.ident; var newLabel = func.labelIdCounter++; if (!func.setjmpTable) func.setjmpTable = []; @@ -1662,11 +1672,13 @@ function analyzer(data, sidePass) { function stackAnalyzer() { data.functions.forEach(function(func) { var lines = func.labels[0].lines; + var hasAlloca = false; for (var i = 0; i < lines.length; i++) { var item = lines[i]; if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.ident)) break; item.allocatedSize = func.variables[item.assignTo].impl === VAR_EMULATED ? calcAllocatedSize(item.allocatedType)*item.ident: 0; + hasAlloca = true; if (USE_TYPED_ARRAYS === 2) { // We need to keep the stack aligned item.allocatedSize = Runtime.forceAlign(item.allocatedSize, Runtime.STACK_ALIGN); @@ -1682,6 +1694,7 @@ function analyzer(data, sidePass) { } func.initialStack = index; func.otherStackAllocations = false; + if (func.initialStack === 0 && hasAlloca) func.otherStackAllocations = true; // a single alloca of zero still requires us to emit stack support code while (func.initialStack == 0) { // one-time loop with possible abort in the middle // If there is no obvious need for stack management, perhaps we don't need it // (we try to optimize that way with SKIP_STACK_IN_SMALL). However, diff --git a/src/compiler.js b/src/compiler.js index aa3c7b928aaaf..e4ce1c886a4ef 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -134,7 +134,7 @@ load('settings.js'); var settings_file = arguments_[0]; var ll_file = arguments_[1]; phase = arguments_[2]; -if (phase == 'pre') { +if (phase == 'pre' || phase == 'glue') { additionalLibraries = Array.prototype.slice.call(arguments_, 3); } else { var forwardedDataFile = arguments_[3]; @@ -206,12 +206,12 @@ if (phase == 'pre') { if (VERBOSE) printErr('VERBOSE is on, this generates a lot of output and can slow down compilation'); // Load struct and define information. -try { +//try { var temp = JSON.parse(read(STRUCT_INFO)); -} catch(e) { - printErr('cannot load struct info at ' + STRUCT_INFO + ' : ' + e + ', trying in current dir'); - temp = JSON.parse(read('struct_info.compiled.json')); -} +//} catch(e) { +// printErr('cannot load struct info at ' + STRUCT_INFO + ' : ' + e + ', trying in current dir'); +// temp = JSON.parse(read('struct_info.compiled.json')); +//} C_STRUCTS = temp.structs; C_DEFINES = temp.defines; @@ -224,12 +224,12 @@ load('analyzer.js'); load('jsifier.js'); if (phase == 'funcs' && RELOOP) { // XXX handle !singlePhase RelooperModule = { TOTAL_MEMORY: ceilPowerOfTwo(2*RELOOPER_BUFFER_SIZE) }; - try { + //try { load(RELOOPER); - } catch(e) { - printErr('cannot load relooper at ' + RELOOPER + ' : ' + e + ', trying in current dir'); - load('relooper.js'); - } + //} catch(e) { + // printErr('cannot load relooper at ' + RELOOPER + ' : ' + e + ', trying in current dir'); + // load('relooper.js'); + //} assert(typeof Relooper != 'undefined'); } globalEval(processMacros(preprocess(read('runtime.js')))); @@ -267,7 +267,7 @@ function compile(raw) { function runPhase(currPhase) { //printErr('// JS compiler in action, phase ' + currPhase + typeof lines + (lines === null)); phase = currPhase; - if (phase != 'pre') { + if (phase != 'pre' && phase != 'glue') { if (singlePhase) PassManager.load(read(forwardedDataFile)); if (phase == 'funcs') { @@ -313,14 +313,16 @@ B = new Benchmarker(); try { if (ll_file) { - if (ll_file.indexOf(String.fromCharCode(10)) == -1) { + if (phase === 'glue') { + compile(';'); + } else if (ll_file.indexOf(String.fromCharCode(10)) == -1) { compile(read(ll_file)); } else { compile(ll_file); // we are given raw .ll } } } catch(err) { - printErr('aborting from js compiler due to exception: ' + err); + printErr('aborting from js compiler due to exception: ' + err + ' | ' + err.stack); } //var M = keys(tokenCacheMisses).map(function(m) { return [m, misses[m]] }).sort(function(a, b) { return a[1] - b[1] }); diff --git a/src/emrun_postjs.js b/src/emrun_postjs.js new file mode 100644 index 0000000000000..eec203ece1497 --- /dev/null +++ b/src/emrun_postjs.js @@ -0,0 +1,20 @@ +function emrun_register_handlers() { + function post(msg) { + var http = new XMLHttpRequest(); + http.open("POST", "stdio.html", true); + http.send(msg); + } + // If the address contains localhost, or we are running the page from port 6931, we can assume we're running the test runner and should post stdout logs. + if (document.URL.search("localhost") != -1 || document.URL.search(":6931/") != -1) { + var emrun_http_sequence_number = 1; + var prevExit = Module['exit']; + var prevPrint = Module['print']; + var prevErr = Module['printErr']; + Module['exit'] = function emrun_exit(returncode) { post('^exit^'+returncode); prevExit(returncode); } + Module['print'] = function emrun_print(text) { post('^out^'+(emrun_http_sequence_number++)+'^'+text); prevPrint(text); } + Module['printErr'] = function emrun_printErr(text) { post('^err^'+(emrun_http_sequence_number++)+'^'+text); prevErr(text); } + } + // Notify emrun web server that this browser has successfully launched the page. + post('^pageload^'); +} +emrun_register_handlers(); diff --git a/src/emrun_prejs.js b/src/emrun_prejs.js new file mode 100644 index 0000000000000..14613c5df223e --- /dev/null +++ b/src/emrun_prejs.js @@ -0,0 +1,5 @@ +// Route URL GET parameters to argc+argv +Module['arguments'] = window.location.search.substr(1).trim().split('&'); +// If no args were passed arguments = [''], in which case kill the single empty string. +if (!Module['arguments'][0]) + Module['arguments'] = []; diff --git a/src/emscripten-source-map.min.js b/src/emscripten-source-map.min.js new file mode 100644 index 0000000000000..9151400f60d93 --- /dev/null +++ b/src/emscripten-source-map.min.js @@ -0,0 +1,31 @@ +function define(e,t,n){if(typeof e!="string")throw new TypeError("Expected string, got: "+e);arguments.length==2&&(n=t);if(e in define.modules)throw new Error("Module already defined: "+e);define.modules[e]=n}function Domain(){this.modules={},this._currentModule=null}define.modules={},function(){function e(e){var t=e.split("/"),n=1;while(nt)-(e0&&t.column>=0&&!n&&!r&&!i)return;if(t&&"line"in t&&"column"in t&&n&&"line"in n&&"column"in n&&t.line>0&&t.column>=0&&n.line>0&&n.column>=0&&r)return;throw new Error("Invalid mapping.")},o.prototype._serializeMappings=function(){var t=0,n=1,i=0,s=0,o=0,u=0,a="",l;this._mappings.sort(f);for(var c=0,h=this._mappings.length;c0){if(!f(l,this._mappings[c-1]))continue;a+=","}a+=r.encode(l.generated.column-t),t=l.generated.column,l.source&&l.original&&(a+=r.encode(this._sources.indexOf(l.source)-u),u=this._sources.indexOf(l.source),a+=r.encode(l.original.line-1-s),s=l.original.line-1,a+=r.encode(l.original.column-i),i=l.original.column,l.name&&(a+=r.encode(this._names.indexOf(l.name)-o),o=this._names.indexOf(l.name)))}return a},o.prototype.toJSON=function(){var t={version:this._version,file:this._file,sources:this._sources.toArray(),names:this._names.toArray(),mappings:this._serializeMappings()};return this._sourceRoot&&(t.sourceRoot=this._sourceRoot),this._sourcesContents&&(t.sourcesContent=t.sources.map(function(e){return t.sourceRoot&&(e=i.relative(t.sourceRoot,e)),Object.prototype.hasOwnProperty.call(this._sourcesContents,i.toSetString(e))?this._sourcesContents[i.toSetString(e)]:null},this)),t},o.prototype.toString=function(){return JSON.stringify(this)},t.SourceMapGenerator=o}),define("source-map/base64-vlq",["require","exports","module","source-map/base64"],function(e,t,n){function a(e){return e<0?(-e<<1)+1:(e<<1)+0}function f(e){var t=(e&1)===1,n=e>>1;return t?-n:n}var r=e("./base64"),i=5,s=1<>>=i,f>0&&(s|=u),n+=r.encode(s);while(f>0);return n},t.decode=function(t){var n=0,s=t.length,a=0,l=0,c,h;do{if(n>=s)throw new Error("Expected more digits in base 64 VLQ value.");h=r.decode(t.charAt(n++)),c=!!(h&u),h&=o,a+=h<=0&&t0)if(c.charAt(0)===";")r++,c=c.slice(1),i=0;else if(c.charAt(0)===",")c=c.slice(1);else{h={},h.generatedLine=r,p=o.decode(c),h.generatedColumn=i+p.value,i=h.generatedColumn,c=p.rest;if(c.length>0&&!l.test(c.charAt(0))){p=o.decode(c),h.source=this._sources.at(a+p.value),a+=p.value,c=p.rest;if(c.length===0||l.test(c.charAt(0)))throw new Error("Found a source, but no line and column");p=o.decode(c),h.originalLine=s+p.value,s=h.originalLine,h.originalLine+=1,c=p.rest;if(c.length===0||l.test(c.charAt(0)))throw new Error("Found a source and line, but no column");p=o.decode(c),h.originalColumn=u+p.value,u=h.originalColumn,c=p.rest,c.length>0&&!l.test(c.charAt(0))&&(p=o.decode(c),h.name=this._names.at(f+p.value),f+=p.value,c=p.rest)}this._generatedMappings.push(h),typeof h.originalLine=="number"&&this._originalMappings.push(h)}this._originalMappings.sort(this._compareOriginalPositions)},u.prototype._compareOriginalPositions=function(t,n){if(t.source>n.source)return 1;if(t.source0?t-o>1?r(o,t,n,i,s):i[o]:o-e>1?r(e,o,n,i,s):e<0?null:i[e]}t.search=function(t,n,i){return n.length>0?r(-1,n.length,t,n,i):null}}),define("source-map/source-node",["require","exports","module","source-map/source-map-generator","source-map/util"],function(e,t,n){function s(e,t,n,r,i){this.children=[],this.sourceContents={},this.line=e===undefined?null:e,this.column=t===undefined?null:t,this.source=n===undefined?null:n,this.name=i===undefined?null:i,r!=null&&this.add(r)}var r=e("./source-map-generator").SourceMapGenerator,i=e("./util");s.fromStringWithSourceMap=function(t,n){function f(e,t){e===null||e.source===undefined?r.add(t):r.add(new s(e.originalLine,e.originalColumn,e.source,t,e.name))}var r=new s,i=t.split("\n"),o=1,u=0,a=null;return n.eachMapping(function(e){if(a===null){while(o=0;n--)this.prepend(t[n]);else{if(!(t instanceof s||typeof t=="string"))throw new TypeError("Expected a SourceNode, string, or an array of SourceNodes and strings. Got "+t);this.children.unshift(t)}return this},s.prototype.walk=function(t){this.children.forEach(function(e){e instanceof s?e.walk(t):e!==""&&t(e,{source:this.source,line:this.line,column:this.column,name:this.name})},this)},s.prototype.join=function(t){var n,r,i=this.children.length;if(i>0){n=[];for(r=0;r 0) { + var call = parseLLVMFunctionCall(part); + EXPORTED_FUNCTIONS[call.ident] = 0; + } + + ret.type = 'i32'; + ret.value = { intertype: 'value', ident: '0', value: '0', type: ret.type }; } else if (!external) { if (item.tokens[1] && item.tokens[1].text != ';') { if (item.tokens[1].text == 'c') { @@ -538,6 +559,7 @@ function intertyper(lines, sidePass, baseLineNums) { ret.value = { intertype: 'value', ident: '0', value: '0', type: ret.type }; } } + return ret; } } @@ -616,7 +638,8 @@ function intertyper(lines, sidePass, baseLineNums) { // 'bitcast' function bitcastHandler(item) { item.intertype = 'bitcast'; - item.type = item.tokens[4].text; // The final type + var last = getTokenIndexByText(item.tokens, ';'); + item.type = item.tokens[Math.min(last, item.tokens.length-1)].text; // The final type Types.needAnalysis[item.type] = 0; var to = getTokenIndexByText(item.tokens, 'to'); item.params = [parseLLVMSegment(item.tokens.slice(1, to))]; diff --git a/src/jsifier.js b/src/jsifier.js index acfb63650078d..6b831b044ace1 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -28,7 +28,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (mainPass) { var shellFile = SHELL_FILE ? SHELL_FILE : (BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'shell_sharedlib.js' : 'shell.js'); - if (phase == 'pre') { + if (phase == 'pre' || phase == 'glue') { // We will start to print out the data, but must do so carefully - we are // dealing with potentially *huge* strings. Convenient replacements and // manipulations may create in-memory copies, and we may OOM. @@ -72,7 +72,7 @@ function JSify(data, functionsOnly, givenFunctions) { LibraryManager.load(); //B.stop('jsifier-libload'); - if (phase == 'pre') { + if (phase == 'pre' || phase == 'glue') { var libFuncsToInclude; if (INCLUDE_FULL_LIBRARY) { assert(!(BUILD_AS_SHARED_LIB || SIDE_MODULE), 'Cannot have both INCLUDE_FULL_LIBRARY and BUILD_AS_SHARED_LIB/SIDE_MODULE set.') @@ -474,7 +474,7 @@ function JSify(data, functionsOnly, givenFunctions) { } } if (SIDE_MODULE) return ';'; // we import into the side module js library stuff from the outside parent - if ((!ASM_JS || phase == 'pre') && + if ((!ASM_JS || phase == 'pre' || phase == 'glue') && (EXPORT_ALL || (ident in EXPORTED_FUNCTIONS))) { contentText += '\nModule["' + ident + '"] = ' + ident + ';'; } @@ -1373,8 +1373,9 @@ function JSify(data, functionsOnly, givenFunctions) { function insertelementHandler(item) { var base = getVectorBaseType(item.type); var ident = ensureVector(item.ident, base); + var laneOp = ((base == 'float') ? 'SIMD.float32x4.with' : 'SIMD.int32x4.with'); //return ident + '.with' + SIMDLane[finalizeLLVMParameter(item.index)] + '(' + finalizeLLVMParameter(item.value) + ')'; - return 'SIMD.with' + SIMDLane[finalizeLLVMParameter(item.index)] + '(' + ident + ',' + finalizeLLVMParameter(item.value) + ')'; + return laneOp + SIMDLane[finalizeLLVMParameter(item.index)] + '(' + ident + ',' + finalizeLLVMParameter(item.value) + ')'; } function extractelementHandler(item) { var base = getVectorBaseType(item.type); @@ -1603,6 +1604,15 @@ function JSify(data, functionsOnly, givenFunctions) { } } + // we alias llvm memset and such to normal memset. The target has a return value, while the original + // does not, so we need to fix that for the actual call target + if (ASM_JS) { + var sig = LibraryManager.library[simpleIdent + '__sig']; + if (sig && sig[0] !== 'v') { + returnType = Functions.getSignatureType(sig[0]); + } + } + if (byPointer) { var sig = Functions.getSignature(returnType, argsTypes, hasVarArgs); if (ASM_JS) { @@ -1704,7 +1714,7 @@ function JSify(data, functionsOnly, givenFunctions) { // if (!mainPass) { - if (phase == 'pre' && !Variables.generatedGlobalBase && !BUILD_AS_SHARED_LIB) { + if ((phase == 'pre' || phase == 'glue') && !Variables.generatedGlobalBase && !BUILD_AS_SHARED_LIB) { Variables.generatedGlobalBase = true; // Globals are done, here is the rest of static memory assert((TARGET_LE32 && Runtime.GLOBAL_BASE == 8) || (TARGET_X86 && Runtime.GLOBAL_BASE == 4)); // this is assumed in e.g. relocations for linkable modules @@ -1719,7 +1729,7 @@ function JSify(data, functionsOnly, givenFunctions) { var generated = itemsDict.function.concat(itemsDict.type).concat(itemsDict.GlobalVariableStub).concat(itemsDict.GlobalVariable); print(generated.map(function(item) { return item.JS; }).join('\n')); - if (phase == 'pre') { + if (phase == 'pre' || phase == 'glue') { if (memoryInitialization.length > 0) { // apply postsets directly into the big memory initialization itemsDict.GlobalVariablePostSet = itemsDict.GlobalVariablePostSet.filter(function(item) { @@ -1742,15 +1752,17 @@ function JSify(data, functionsOnly, givenFunctions) { }); // write out the singleton big memory initialization value print('/* memory initializer */ ' + makePointer(memoryInitialization, null, 'ALLOC_NONE', 'i8', 'Runtime.GLOBAL_BASE' + (SIDE_MODULE ? '+H_BASE' : ''), true)); - } else { + } else if (phase !== 'glue') { print('/* no memory initializer */'); // test purposes } - // Define postsets. These will be run in ATINIT, right before global initializers (which might need the postsets). We cannot - // run them now because the memory initializer might not have been applied yet. - print('function runPostSets() {\n'); - print(itemsDict.GlobalVariablePostSet.map(function(item) { return item.JS }).join('\n')); - print('}\n'); + if (phase !== 'glue') { + // Define postsets. These will be run in ATINIT, right before global initializers (which might need the postsets). We cannot + // run them now because the memory initializer might not have been applied yet. + print('function runPostSets() {\n'); + print(itemsDict.GlobalVariablePostSet.map(function(item) { return item.JS }).join('\n')); + print('}\n'); + } if (USE_TYPED_ARRAYS == 2) { if (!BUILD_AS_SHARED_LIB && !SIDE_MODULE) { @@ -1780,7 +1792,7 @@ function JSify(data, functionsOnly, givenFunctions) { } // Print out global variables and postsets TODO: batching - if (phase == 'pre') { + if (phase == 'pre' || phase == 'glue') { var legalizedI64sDefault = legalizedI64s; legalizedI64s = false; @@ -1847,10 +1859,10 @@ function JSify(data, functionsOnly, givenFunctions) { // first row are utilities called from generated code, second are needed from fastLong ['i64Add', 'i64Subtract', 'bitshift64Shl', 'bitshift64Lshr', 'bitshift64Ashr', 'llvm_ctlz_i32', 'llvm_cttz_i32'].forEach(function(func) { - if (!Functions.libraryFunctions[func]) { + if (!Functions.libraryFunctions[func] || (phase == 'glue' && func[0] === 'l' && !addedLibraryItems[func])) { // TODO: one-by-one in fastcomp glue mode print(processLibraryFunction(LibraryManager.library[func], func)); // must be first to be close to generated code Functions.implementedFunctions['_' + func] = LibraryManager.library[func + '__sig']; - Functions.libraryFunctions[func] = 1; + Functions.libraryFunctions[func] = phase == 'glue' ? 2 : 1; // XXX // limited dependency handling var deps = LibraryManager.library[func + '__deps']; if (deps) { diff --git a/src/library.js b/src/library.js index a5380c3a2e501..fc731e01fb09a 100644 --- a/src/library.js +++ b/src/library.js @@ -23,6 +23,7 @@ LibraryManager.library = { stdout: 'allocate(1, "i32*", ALLOC_STATIC)', stderr: 'allocate(1, "i32*", ALLOC_STATIC)', _impure_ptr: 'allocate(1, "i32*", ALLOC_STATIC)', + __dso_handle: 'allocate(1, "i32*", ALLOC_STATIC)', // ========================================================================== // dirent.h @@ -471,6 +472,11 @@ LibraryManager.library = { mkstemp: function(template) { return _creat(_mktemp(template), 0600); }, + mkdtemp__deps: ['mktemp', 'mkdir'], + mkdtemp: function(template) { + template = _mktemp(template); + return (_mkdir(template, 0700) === 0) ? template : 0; + }, fcntl__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], fcntl: function(fildes, cmd, varargs, dup2) { // int fcntl(int fildes, int cmd, ...); @@ -535,7 +541,7 @@ LibraryManager.library = { // Advise as much as you wish. We don't care. return 0; }, - posix_madvise: 'posix_fadvise', + posix_madvise: function(){ return 0 }, // ditto as fadvise posix_fallocate__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], posix_fallocate: function(fd, offset, len) { // int posix_fallocate(int fd, off_t offset, off_t len); @@ -1855,17 +1861,20 @@ LibraryManager.library = { // int x = 4; printf("%c\n", (char)x); var ret; if (type === 'double') { +#if TARGET_LE32 == 2 + ret = {{{ makeGetValue('varargs', 'argIndex', 'double', undefined, undefined, true, 4) }}}; +#else ret = {{{ makeGetValue('varargs', 'argIndex', 'double', undefined, undefined, true) }}}; +#endif #if USE_TYPED_ARRAYS == 2 } else if (type == 'i64') { - -#if TARGET_LE32 +#if TARGET_LE32 == 1 ret = [{{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}}, {{{ makeGetValue('varargs', 'argIndex+8', 'i32', undefined, undefined, true) }}}]; argIndex += {{{ STACK_ALIGN }}}; // each 32-bit chunk is in a 64-bit block #else - ret = [{{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}}, - {{{ makeGetValue('varargs', 'argIndex+4', 'i32', undefined, undefined, true) }}}]; + ret = [{{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true, 4) }}}, + {{{ makeGetValue('varargs', 'argIndex+4', 'i32', undefined, undefined, true, 4) }}}]; #endif #else @@ -1876,7 +1885,11 @@ LibraryManager.library = { type = 'i32'; // varargs are always i32, i64, or double ret = {{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}}; } +#if TARGET_LE32 == 2 + argIndex += Runtime.getNativeFieldSize(type); +#else argIndex += Math.max(Runtime.getNativeFieldSize(type), Runtime.getAlignSize(type, null, true)); +#endif return ret; } @@ -2505,6 +2518,10 @@ LibraryManager.library = { } var bytesRead = 0; var streamObj = FS.getStream(stream); + if (!streamObj) { + ___setErrNo(ERRNO_CODES.EBADF); + return 0; + } while (streamObj.ungotten.length && bytesToRead > 0) { {{{ makeSetValue('ptr++', '0', 'streamObj.ungotten.pop()', 'i8') }}} bytesToRead--; @@ -3522,13 +3539,15 @@ LibraryManager.library = { llvm_memcpy_p0i8_p0i8_i32: 'memcpy', llvm_memcpy_p0i8_p0i8_i64: 'memcpy', - memmove__sig: 'viii', + memmove__sig: 'iiii', memmove__asm: true, memmove__deps: ['memcpy'], memmove: function(dest, src, num) { dest = dest|0; src = src|0; num = num|0; + var ret = 0; if (((src|0) < (dest|0)) & ((dest|0) < ((src + num)|0))) { // Unlikely case: Copy backwards in a safe manner + ret = dest; src = (src + num)|0; dest = (dest + num)|0; while ((num|0) > 0) { @@ -3537,9 +3556,11 @@ LibraryManager.library = { num = (num - 1)|0; {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}}; } + dest = ret; } else { _memcpy(dest, src, num) | 0; } + return dest | 0; }, llvm_memmove_i32: 'memmove', llvm_memmove_i64: 'memmove', @@ -3556,7 +3577,7 @@ LibraryManager.library = { memset__inline: function(ptr, value, num, align) { return makeSetValues(ptr, 0, value, 'null', num, align); }, - memset__sig: 'viii', + memset__sig: 'iiii', memset__asm: true, memset: function(ptr, value, num) { #if USE_TYPED_ARRAYS == 2 @@ -3585,8 +3606,10 @@ LibraryManager.library = { {{{ makeSetValueAsm('ptr', 0, 'value', 'i8') }}}; ptr = (ptr+1)|0; } + return (ptr-num)|0; #else {{{ makeSetValues('ptr', '0', 'value', 'null', 'num') }}}; + return ptr; #endif }, llvm_memset_i32: 'memset', @@ -4657,6 +4680,10 @@ LibraryManager.library = { llvm_dbg_declare__inline: function() { throw 'llvm_debug_declare' }, // avoid warning + // llvm-nacl + + llvm_nacl_atomic_store_i32__inline: true, + // ========================================================================== // llvm-mono integration // ========================================================================== @@ -6955,7 +6982,7 @@ LibraryManager.library = { pthread_setspecific__deps: ['$PTHREAD_SPECIFIC', '$ERRNO_CODES'], pthread_setspecific: function(key, value) { - if (value == 0) { + if (!(key in PTHREAD_SPECIFIC)) { return ERRNO_CODES.EINVAL; } PTHREAD_SPECIFIC[key] = value; @@ -7674,6 +7701,94 @@ LibraryManager.library = { return _gai_strerror.buffer; }, + // Implement netdb.h protocol entry (getprotoent, getprotobyname, getprotobynumber, setprotoent, endprotoent) + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/getprotobyname.html + // The Protocols object holds our 'fake' protocols 'database'. + $Protocols: { + list: [], + map: {} + }, + setprotoent__deps: ['$Protocols'], + setprotoent: function(stayopen) { + // void setprotoent(int stayopen); + + // Allocate and populate a protoent structure given a name, protocol number and array of aliases + function allocprotoent(name, proto, aliases) { + // write name into buffer + var nameBuf = _malloc(name.length + 1); + writeAsciiToMemory(name, nameBuf); + + // write aliases into buffer + var j = 0; + var length = aliases.length; + var aliasListBuf = _malloc((length + 1) * 4); // Use length + 1 so we have space for the terminating NULL ptr. + + for (var i = 0; i < length; i++, j += 4) { + var alias = aliases[i]; + var aliasBuf = _malloc(alias.length + 1); + writeAsciiToMemory(alias, aliasBuf); + {{{ makeSetValue('aliasListBuf', 'j', 'aliasBuf', 'i8*') }}}; + } + {{{ makeSetValue('aliasListBuf', 'j', '0', 'i8*') }}}; // Terminating NULL pointer. + + // generate protoent + var pe = _malloc({{{ C_STRUCTS.protoent.__size__ }}}); + {{{ makeSetValue('pe', C_STRUCTS.protoent.p_name, 'nameBuf', 'i8*') }}}; + {{{ makeSetValue('pe', C_STRUCTS.protoent.p_aliases, 'aliasListBuf', 'i8**') }}}; + {{{ makeSetValue('pe', C_STRUCTS.protoent.p_proto, 'proto', 'i32') }}}; + return pe; + }; + + // Populate the protocol 'database'. The entries are limited to tcp and udp, though it is fairly trivial + // to add extra entries from /etc/protocols if desired - though not sure if that'd actually be useful. + var list = Protocols.list; + var map = Protocols.map; + if (list.length === 0) { + var entry = allocprotoent('tcp', 6, ['TCP']); + list.push(entry); + map['tcp'] = map['6'] = entry; + entry = allocprotoent('udp', 17, ['UDP']); + list.push(entry); + map['udp'] = map['17'] = entry; + } + + _setprotoent.index = 0; + }, + + endprotoent: function() { + // void endprotoent(void); + // We're not using a real protocol database so we don't do a real close. + }, + + getprotoent__deps: ['setprotoent', '$Protocols'], + getprotoent: function(number) { + // struct protoent *getprotoent(void); + // reads the next entry from the protocols 'database' or return NULL if 'eof' + if (_setprotoent.index === Protocols.list.length) { + return 0; + } else { + var result = Protocols.list[_setprotoent.index++]; + return result; + } + }, + + getprotobyname__deps: ['setprotoent', '$Protocols'], + getprotobyname: function(name) { + // struct protoent *getprotobyname(const char *); + name = Pointer_stringify(name); + _setprotoent(true); + var result = Protocols.map[name]; + return result; + }, + + getprotobynumber__deps: ['setprotoent', '$Protocols'], + getprotobynumber: function(number) { + // struct protoent *getprotobynumber(int proto); + _setprotoent(true); + var result = Protocols.map[number]; + return result; + }, + // ========================================================================== // sockets. Note that the implementation assumes all sockets are always // nonblocking @@ -8720,12 +8835,263 @@ LibraryManager.library = { } }, + // Returns [parentFuncArguments, functionName, paramListName] + _emscripten_traverse_stack: function(args) { + if (!args || !args.callee || !args.callee.name) { + return [null, '', '']; + } + + var funstr = args.callee.toString(); + var funcname = args.callee.name; + var str = '('; + var first = true; + for(i in args) { + var a = args[i]; + if (!first) { + str += ", "; + } + first = false; + if (typeof a === 'number' || typeof a === 'string') { + str += a; + } else { + str += '(' + typeof a + ')'; + } + } + str += ')'; + args = args.callee.caller.arguments; + if (first) + str = ''; + return [args, funcname, str]; + }, + + emscripten_get_callstack_js__deps: ['_emscripten_traverse_stack'], + emscripten_get_callstack_js: function(flags) { + var err = new Error(); + if (!err.stack) { + Runtime.warnOnce('emscripten_get_callstack_js is not supported on this browser!'); + return ''; + } + var callstack = new Error().stack.toString(); + + // Find the symbols in the callstack that corresponds to the functions that report callstack information, and remove everyhing up to these from the output. + var iThisFunc = callstack.lastIndexOf('_emscripten_log'); + var iThisFunc2 = callstack.lastIndexOf('_emscripten_get_callstack'); + var iNextLine = callstack.indexOf('\n', Math.max(iThisFunc, iThisFunc2))+1; + callstack = callstack.slice(iNextLine); + + // If user requested to see the original source stack, but no source map information is available, just fall back to showing the JS stack. + if (flags & 8/*EM_LOG_C_STACK*/ && typeof emscripten_source_map === 'undefined') { + Runtime.warnOnce('Source map information is not available, emscripten_log with EM_LOG_C_STACK will be ignored. Build with "--pre-js $EMSCRIPTEN/src/emscripten-source-map.min.js" linker flag to add source map loading to code.'); + flags ^= 8/*EM_LOG_C_STACK*/; + flags |= 16/*EM_LOG_JS_STACK*/; + } + + var stack_args = null; + if (flags & 128 /*EM_LOG_FUNC_PARAMS*/) { + // To get the actual parameters to the functions, traverse the stack via the unfortunately deprecated 'arguments.callee' method, if it works: + var stack_args = __emscripten_traverse_stack(arguments); + while (stack_args[1].indexOf('_emscripten_') >= 0) + stack_args = __emscripten_traverse_stack(stack_args[0]); + } + + // Process all lines: + lines = callstack.split('\n'); + callstack = ''; + var firefoxRe = new RegExp('\\s*(.*?)@(.*):(.*)'); // Extract components of form ' Object._main@http://server.com:4324' + var chromeRe = new RegExp('\\s*at (.*?) \\\((.*):(.*):(.*)\\\)'); // Extract components of form ' at Object._main (http://server.com/file.html:4324:12)' + + for(l in lines) { + var line = lines[l]; + + var jsSymbolName = ''; + var file = ''; + var lineno = 0; + var column = 0; + + var parts = chromeRe.exec(line); + if (parts && parts.length == 5) { + jsSymbolName = parts[1]; + file = parts[2]; + lineno = parts[3]; + column = parts[4]; + } else { + parts = firefoxRe.exec(line); + if (parts && parts.length == 4) { + jsSymbolName = parts[1]; + file = parts[2]; + lineno = parts[3]; + column = 0; // Firefox doesn't carry column information. See https://bugzilla.mozilla.org/show_bug.cgi?id=762556 + } else { + // Was not able to extract this line for demangling/sourcemapping purposes. Output it as-is. + callstack += line + '\n'; + continue; + } + } + + // Try to demangle the symbol, but fall back to showing the original JS symbol name if not available. + var cSymbolName = (flags & 32/*EM_LOG_DEMANGLE*/) ? demangle(jsSymbolName) : jsSymbolName; + if (!cSymbolName) { + cSymbolName = jsSymbolName; + } + + var haveSourceMap = false; + + if (flags & 8/*EM_LOG_C_STACK*/) { + var orig = emscripten_source_map.originalPositionFor({line: lineno, column: column}); + haveSourceMap = (orig && orig.source); + if (haveSourceMap) { + if (flags & 64/*EM_LOG_NO_PATHS*/) { + orig.source = orig.source.substring(orig.source.replace(/\\/g, "/").lastIndexOf('/')+1); + } + callstack += ' at ' + cSymbolName + ' (' + orig.source + ':' + orig.line + ':' + orig.column + ')\n'; + } + } + if ((flags & 16/*EM_LOG_JS_STACK*/) || !haveSourceMap) { + if (flags & 64/*EM_LOG_NO_PATHS*/) { + file = file.substring(file.replace(/\\/g, "/").lastIndexOf('/')+1); + } + callstack += (haveSourceMap ? (' = '+jsSymbolName) : (' at '+cSymbolName)) + ' (' + file + ':' + lineno + ':' + column + ')\n'; + } + + // If we are still keeping track with the callstack by traversing via 'arguments.callee', print the function parameters as well. + if (flags & 128 /*EM_LOG_FUNC_PARAMS*/ && stack_args[0]) { + if (stack_args[1] == jsSymbolName && stack_args[2].length > 0) { + callstack = callstack.replace(/\s+$/, ''); + callstack += ' with values: ' + stack_args[1] + stack_args[2] + '\n'; + } + stack_args = __emscripten_traverse_stack(stack_args[0]); + } + } + // Trim extra whitespace at the end of the output. + callstack = callstack.replace(/\s+$/, ''); + return callstack; + }, + + emscripten_get_callstack__deps: ['emscripten_get_callstack_js'], + emscripten_get_callstack: function(flags, str, maxbytes) { + var callstack = _emscripten_get_callstack_js(flags); + // User can query the required amount of bytes to hold the callstack. + if (!str || maxbytes <= 0) { + return callstack.length+1; + } + // Truncate output to avoid writing past bounds. + if (callstack.length > maxbytes-1) { + callstack.slice(0, maxbytes-1); + } + // Output callstack string as C string to HEAP. + writeStringToMemory(callstack, str, false); + + // Return number of bytes written. + return callstack.length+1; + }, + + emscripten_log_js__deps: ['emscripten_get_callstack_js'], + emscripten_log_js: function(flags, str) { + if (flags & 24/*EM_LOG_C_STACK | EM_LOG_JS_STACK*/) { + str = str.replace(/\s+$/, ''); // Ensure the message and the callstack are joined cleanly with exactly one newline. + str += (str.length > 0 ? '\n' : '') + _emscripten_get_callstack_js(flags); + } + + if (flags & 1 /*EM_LOG_CONSOLE*/) { + if (flags & 4 /*EM_LOG_ERROR*/) { + console.error(str); + } else if (flags & 2 /*EM_LOG_WARN*/) { + console.warn(str); + } else { + console.log(str); + } + } else if (flags & 6 /*EM_LOG_ERROR|EM_LOG_WARN*/) { + Module.printErr(str); + } else { + Module.print(str); + } + }, + + emscripten_log__deps: ['_formatString', 'emscripten_log_js'], + emscripten_log: function(flags, varargs) { + // Extract the (optionally-existing) printf format specifier field from varargs. + var format = {{{ makeGetValue('varargs', '0', 'i32', undefined, undefined, true) }}}; + varargs += Math.max(Runtime.getNativeFieldSize('i32'), Runtime.getAlignSize('i32', null, true)); + var str = ''; + if (format) { + var result = __formatString(format, varargs); + for(var i = 0 ; i < result.length; ++i) { + str += String.fromCharCode(result[i]); + } + } + _emscripten_log_js(flags, str); + }, + //============================ // emscripten vector ops //============================ - emscripten_float32x4_signmask__inline: function(x) { - return x + '.signMask()'; + emscripten_float32x4_signmask__inline: function(a) { + return 'SIMD.float32x4.bitsToInt32x4(' + a + ').signMask'; + }, + + emscripten_float32x4_min__inline: function(a, b) { + return 'SIMD.float32x4.min(' + a + ', ' + b + ')'; + }, + + emscripten_float32x4_max__inline: function(a, b) { + return 'SIMD.float32x4.max(' + a + ', ' + b + ')'; + }, + + emscripten_float32x4_sqrt__inline: function(a) { + return 'SIMD.float32x4.sqrt(' + a + ')'; + }, + + emscripten_float32x4_lessThan__inline: function(a, b) { + return 'SIMD.int32x4.bitsToFloat32x4(SIMD.float32x4.lessThan(' + a + ', ' + b + '))'; + }, + + emscripten_float32x4_lessThanOrEqual__inline: function(a, b) { + return 'SIMD.int32x4.bitsToFloat32x4(SIMD.float32x4.lessThanOrEqual(' + a + ', ' + b + '))'; + }, + + emscripten_float32x4_equal__inline: function(a, b) { + return 'SIMD.int32x4.bitsToFloat32x4(SIMD.float32x4.equal(' + a + ', ' + b + '))'; + }, + + emscripten_float32x4_greaterThanOrEqual__inline: function(a, b) { + return 'SIMD.int32x4.bitsToFloat32x4(SIMD.float32x4.greaterThanOrEqual(' + a + ', ' + b + '))'; + }, + + emscripten_float32x4_greaterThan__inline: function(a, b) { + return 'SIMD.int32x4.bitsToFloat32x4(SIMD.float32x4.greaterThan(' + a + ', ' + b + '))'; + }, + + emscripten_float32x4_and__inline: function(a, b) { + return 'SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.and(SIMD.float32x4.bitsToInt32x4(' + a + '), SIMD.float32x4.bitsToInt32x4(' + b + ')))'; + }, + + emscripten_float32x4_andNot__inline: function(a, b) { + return 'SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.and(SIMD.int32x4.not(SIMD.float32x4.bitsToInt32x4(' + a + ')), SIMD.float32x4.bitsToInt32x4(' + b + ')))'; + }, + + emscripten_float32x4_or__inline: function(a, b) { + return 'SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.or(SIMD.float32x4.bitsToInt32x4(' + a + '), SIMD.float32x4.bitsToInt32x4(' + b + ')))'; + }, + + emscripten_float32x4_xor__inline: function(a, b) { + return 'SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.xor(SIMD.float32x4.bitsToInt32x4(' + a + '), SIMD.float32x4.bitsToInt32x4(' + b + ')))'; + }, + + emscripten_int32x4_bitsToFloat32x4__inline: function(a) { + return 'SIMD.int32x4.bitsToFloat32x4(' + a + ')'; + }, + + emscripten_int32x4_toFloat32x4__inline: function(a) { + return 'SIMD.int32x4.toFloat32x4(' + a + ')'; + }, + + emscripten_float32x4_bitsToInt32x4__inline: function(a) { + return 'SIMD.float32x4.bitsToInt32x4(' + a + ')'; + }, + + emscripten_float32x4_toInt32x4__inline: function(a) { + return 'SIMD.float32x4.toInt32x4(' + a + ')'; }, //============================ diff --git a/src/library_browser.js b/src/library_browser.js index 8444fb73281c4..b368c6ac8ba1d 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -250,15 +250,24 @@ mergeInto(LibraryManager.library, { contextAttributes.preserveDrawingBuffer = true; #endif - ['experimental-webgl', 'webgl'].some(function(webglId) { - return ctx = canvas.getContext(webglId, contextAttributes); - }); + var errorInfo = '?'; + function onContextCreationError(event) { + errorInfo = event.statusMessage || errorInfo; + } + canvas.addEventListener('webglcontextcreationerror', onContextCreationError, false); + try { + ['experimental-webgl', 'webgl'].some(function(webglId) { + return ctx = canvas.getContext(webglId, contextAttributes); + }); + } finally { + canvas.removeEventListener('webglcontextcreationerror', onContextCreationError, false); + } } else { ctx = canvas.getContext('2d'); } if (!ctx) throw ':('; } catch (e) { - Module.print('Could not create canvas - ' + e); + Module.print('Could not create canvas: ' + [errorInfo, e]); return null; } if (useWebGL) { @@ -854,7 +863,7 @@ mergeInto(LibraryManager.library, { var styleSheet = document.styleSheets[0]; var rules = styleSheet.cssRules; for (var i = 0; i < rules.length; i++) { - if (rules[i].cssText.substr(0, 5) == 'canvas') { + if (rules[i].cssText.substr(0, 6) == 'canvas') { styleSheet.deleteRule(i); i--; } diff --git a/src/library_egl.js b/src/library_egl.js index 73d5e5441477e..11cf8951371c5 100644 --- a/src/library_egl.js +++ b/src/library_egl.js @@ -264,6 +264,26 @@ var LibraryEGL = { return 0; } + // EGL 1.4 spec says default EGL_CONTEXT_CLIENT_VERSION is GLES1, but this is not supported by Emscripten. + // So user must pass EGL_CONTEXT_CLIENT_VERSION == 2 to initialize EGL. + var glesContextVersion = 1; + for(;;) { + var param = {{{ makeGetValue('contextAttribs', '0', 'i32') }}}; + if (!param) break; + var value = {{{ makeGetValue('contextAttribs', '4', 'i32') }}}; + if (param == 0x3098 /*EGL_CONTEXT_CLIENT_VERSION*/) { + glesContextVersion = value; + } + contextAttribs += 8; + } + if (glesContextVersion != 2) { +#if GL_ASSERTIONS + Module.printErr('When initializing GLES2/WebGL1 via EGL, one must pass EGL_CONTEXT_CLIENT_VERSION = 2 to GL context attributes! GLES version ' + glesContextVersion + ' is not supported!'); +#endif + EGL.setErrorCode(0x3005 /* EGL_BAD_CONFIG */); + return 0; /* EGL_NO_CONTEXT */ + } + _glutInitDisplayMode(0x92 /* GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE */); EGL.windowID = _glutCreateWindow(); if (EGL.windowID != 0) { diff --git a/src/library_fs.js b/src/library_fs.js index 5412185ffde46..1e7856aa4d7ea 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -961,7 +961,7 @@ mergeInto(LibraryManager.library, { throw new FS.ErrnoError(ERRNO_CODES.EACCES); } if (!stream.stream_ops.mmap) { - throw new FS.errnoError(ERRNO_CODES.ENODEV); + throw new FS.ErrnoError(ERRNO_CODES.ENODEV); } return stream.stream_ops.mmap(stream, buffer, offset, length, position, prot, flags); }, diff --git a/src/library_gl.js b/src/library_gl.js index afd36197688b3..29f78c8a74df7 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -209,6 +209,114 @@ var LibraryGL = { ((height - 1) * alignedRowSize + plainRowSize); }, + get: function(name_, p, type) { + // Guard against user passing a null pointer. + // Note that GLES2 spec does not say anything about how passing a null pointer should be treated. + // Testing on desktop core GL 3, the application crashes on glGetIntegerv to a null pointer, but + // better to report an error instead of doing anything random. + if (!p) { +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_VALUE in glGet' + type + 'v(name=' + name_ + ': Function called with null out pointer!'); +#endif + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } + var ret = undefined; + switch(name_) { // Handle a few trivial GLES values + case 0x8DFA: // GL_SHADER_COMPILER + ret = 1; + break; + case 0x8DF8: // GL_SHADER_BINARY_FORMATS + if (type !== 'Integer') { + GL.recordError(0x0500); // GL_INVALID_ENUM +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGet' + type + 'v(GL_SHADER_BINARY_FORMATS): Invalid parameter type!'); +#endif + } + return; // Do not write anything to the out pointer, since no binary formats are supported. + case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS + ret = 0; + break; + case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS + // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be queried for length), + // so implement it ourselves to allow C++ GLES2 code get the length. + var formats = Module.ctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/); + ret = formats.length; + break; + case 0x8B9A: // GL_IMPLEMENTATION_COLOR_READ_TYPE + ret = 0x1401; // GL_UNSIGNED_BYTE + break; + case 0x8B9B: // GL_IMPLEMENTATION_COLOR_READ_FORMAT + ret = 0x1908; // GL_RGBA + break; + } + + if (ret === undefined) { + var result = Module.ctx.getParameter(name_); + switch (typeof(result)) { + case "number": + ret = result; + break; + case "boolean": + ret = result ? 1 : 0; + break; + case "string": + GL.recordError(0x0500); // GL_INVALID_ENUM +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGet' + type + 'v(' + name_ + ') on a name which returns a string!'); +#endif + return; + case "object": + if (result === null) { + GL.recordError(0x0500); // GL_INVALID_ENUM +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGet' + type + 'v(' + name_ + ') and it returns null!'); +#endif + return; + } else if (result instanceof Float32Array || + result instanceof Uint32Array || + result instanceof Int32Array || + result instanceof Array) { + for (var i = 0; i < result.length; ++i) { + switch (type) { + case 'Integer': {{{ makeSetValue('p', 'i*4', 'result[i]', 'i32') }}}; break; + case 'Float': {{{ makeSetValue('p', 'i*4', 'result[i]', 'float') }}}; break; + case 'Boolean': {{{ makeSetValue('p', 'i', 'result[i] ? 1 : 0', 'i8') }}}; break; + default: throw 'internal glGet error, bad type: ' + type; + } + } + return; + } else if (result instanceof WebGLBuffer || + result instanceof WebGLProgram || + result instanceof WebGLFramebuffer || + result instanceof WebGLRenderbuffer || + result instanceof WebGLTexture) { + ret = result.name | 0; + } else { + GL.recordError(0x0500); // GL_INVALID_ENUM +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGet' + type + 'v: Unknown object returned from WebGL getParameter(' + name_ + ')!'); +#endif + return; + } + break; + default: + GL.recordError(0x0500); // GL_INVALID_ENUM +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Native code calling glGet' + type + 'v(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!'); +#endif + return; + } + } + + switch (type) { + case 'Integer': {{{ makeSetValue('p', '0', 'ret', 'i32') }}}; break; + case 'Float': {{{ makeSetValue('p', '0', 'ret', 'float') }}}; break; + case 'Boolean': {{{ makeSetValue('p', '0', 'ret ? 1 : 0', 'i8') }}}; break; + default: throw 'internal glGet error, bad type: ' + type; + } + }, + getTexPixelData: function(type, format, width, height, pixels, internalFormat) { var sizePerPixel; switch (type) { @@ -288,6 +396,22 @@ var LibraryGL = { } }, +#if GL_FFP_ONLY + enabledClientAttribIndices: [], + enableVertexAttribArray: function enableVertexAttribArray(index) { + if (!GL.enabledClientAttribIndices[index]) { + GL.enabledClientAttribIndices[index] = true; + Module.ctx.enableVertexAttribArray(index); + } + }, + disableVertexAttribArray: function disableVertexAttribArray(index) { + if (GL.enabledClientAttribIndices[index]) { + GL.enabledClientAttribIndices[index] = false; + Module.ctx.disableVertexAttribArray(index); + } + }, +#endif + #if FULL_ES2 calcBufLength: function calcBufLength(size, type, stride, count) { if (stride > 0) { @@ -554,214 +678,17 @@ var LibraryGL = { glGetIntegerv__sig: 'vii', glGetIntegerv: function(name_, p) { - switch(name_) { // Handle a few trivial GLES values - case 0x8DFA: // GL_SHADER_COMPILER - {{{ makeSetValue('p', '0', '1', 'i32') }}}; - return; - case 0x8DF8: // GL_SHADER_BINARY_FORMATS - case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS - {{{ makeSetValue('p', '0', '0', 'i32') }}}; - return; - case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS - // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be queried for length), - // so implement it ourselves to allow C++ GLES2 code get the length. - var formats = Module.ctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/); - {{{ makeSetValue('p', '0', 'formats.length', 'i32') }}}; - return; - } - var result = Module.ctx.getParameter(name_); - switch (typeof(result)) { - case "number": - {{{ makeSetValue('p', '0', 'result', 'i32') }}}; - break; - case "boolean": - {{{ makeSetValue('p', '0', 'result ? 1 : 0', 'i8') }}}; - break; - case "string": - GL.recordError(0x0500/*GL_INVALID_ENUM*/); -#if GL_ASSERTIONS - Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Native code calling glGetIntegerv(' + name_ + ') on a name which returns a string!'); -#endif - return; - case "object": - if (result === null) { - {{{ makeSetValue('p', '0', '0', 'i32') }}}; - } else if (result instanceof Float32Array || - result instanceof Uint32Array || - result instanceof Int32Array || - result instanceof Array) { - for (var i = 0; i < result.length; ++i) { - {{{ makeSetValue('p', 'i*4', 'result[i]', 'i32') }}}; - } - } else if (result instanceof WebGLBuffer) { - {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}}; - } else if (result instanceof WebGLProgram) { - {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}}; - } else if (result instanceof WebGLFramebuffer) { - {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}}; - } else if (result instanceof WebGLRenderbuffer) { - {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}}; - } else if (result instanceof WebGLTexture) { - {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}}; - } else { - GL.recordError(0x0500/*GL_INVALID_ENUM*/); -#if GL_ASSERTIONS - Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Unknown object returned from WebGL getParameter(' + name_ + ')!'); -#endif - return; - } - break; - default: - GL.recordError(0x0500/*GL_INVALID_ENUM*/); -#if GL_ASSERTIONS - Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Native code calling glGetIntegerv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!'); -#endif - return; - } + return GL.get(name_, p, 'Integer'); }, glGetFloatv__sig: 'vii', glGetFloatv: function(name_, p) { - switch(name_) { - case 0x8DFA: // GL_SHADER_COMPILER - {{{ makeSetValue('p', '0', '1', 'float') }}}; - return; - case 0x8DF8: // GL_SHADER_BINARY_FORMATS - GL.recordError(0x0500/*GL_INVALID_ENUM*/); -#if GL_ASSERTIONS - Module.printErr('GL_INVALID_ENUM in glGetFloatv(GL_SHADER_BINARY_FORMATS): Invalid parameter type!'); -#endif - return; - case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS - {{{ makeSetValue('p', '0', '0', 'float') }}}; - return; - case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS - // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be queried for length), - // so implement it ourselves to allow C++ GLES2 code get the length. - var formats = Module.ctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/); - {{{ makeSetValue('p', '0', 'formats.length', 'float') }}}; - return; - } - - var result = Module.ctx.getParameter(name_); - switch (typeof(result)) { - case "number": - {{{ makeSetValue('p', '0', 'result', 'float') }}}; - break; - case "boolean": - {{{ makeSetValue('p', '0', 'result ? 1.0 : 0.0', 'float') }}}; - break; - case "string": - {{{ makeSetValue('p', '0', '0', 'float') }}}; - case "object": - if (result === null) { - GL.recordError(0x0500/*GL_INVALID_ENUM*/); -#if GL_ASSERTIONS - Module.printErr('GL_INVALID_ENUM in glGetFloatv: Native code calling glGetFloatv(' + name_ + ') and it returns null!'); -#endif - return; - } else if (result instanceof Float32Array || - result instanceof Uint32Array || - result instanceof Int32Array || - result instanceof Array) { - for (var i = 0; i < result.length; ++i) { - {{{ makeSetValue('p', 'i*4', 'result[i]', 'float') }}}; - } - } else if (result instanceof WebGLBuffer) { - {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}}; - } else if (result instanceof WebGLProgram) { - {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}}; - } else if (result instanceof WebGLFramebuffer) { - {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}}; - } else if (result instanceof WebGLRenderbuffer) { - {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}}; - } else if (result instanceof WebGLTexture) { - {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}}; - } else { - GL.recordError(0x0500/*GL_INVALID_ENUM*/); -#if GL_ASSERTIONS - Module.printErr('GL_INVALID_ENUM in glGetFloatv: Native code calling glGetFloatv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!'); -#endif - return; - } - break; - default: - GL.recordError(0x0500/*GL_INVALID_ENUM*/); -#if GL_ASSERTIONS - Module.printErr('GL_INVALID_ENUM in glGetFloatv: Native code calling glGetFloatv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!'); -#endif - return; - } + return GL.get(name_, p, 'Float'); }, glGetBooleanv__sig: 'vii', glGetBooleanv: function(name_, p) { - switch(name_) { - case 0x8DFA: // GL_SHADER_COMPILER - {{{ makeSetValue('p', '0', '1', 'i8') }}}; - return; - case 0x8DF8: // GL_SHADER_BINARY_FORMATS - GL.recordError(0x0500/*GL_INVALID_ENUM*/); -#if GL_ASSERTIONS - Module.printErr('GL_INVALID_ENUM in glGetBooleanv(GL_SHADER_BINARY_FORMATS): Invalid parameter type!'); -#endif - return; - case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS - {{{ makeSetValue('p', '0', '0', 'i8') }}}; - return; - case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS - // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be queried for length), - // so implement it ourselves to allow C++ GLES2 code get the length. - var hasCompressedFormats = Module.ctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/).length > 0 ? 1 : 0; - {{{ makeSetValue('p', '0', 'hasCompressedFormats', 'i8') }}}; - return; - } - - var result = Module.ctx.getParameter(name_); - switch (typeof(result)) { - case "number": - {{{ makeSetValue('p', '0', 'result != 0', 'i8') }}}; - break; - case "boolean": - {{{ makeSetValue('p', '0', 'result != 0', 'i8') }}}; - break; - case "string": - GL.recordError(0x0500/*GL_INVALID_ENUM*/); -#if GL_ASSERTIONS - Module.printErr('GL_INVALID_ENUM in glGetBooleanv: Native code calling glGetBooleanv(' + name_ + ') on a name which returns a string!'); -#endif - return; - case "object": - if (result === null) { - {{{ makeSetValue('p', '0', '0', 'i8') }}}; - } else if (result instanceof Float32Array || - result instanceof Uint32Array || - result instanceof Int32Array || - result instanceof Array) { - for (var i = 0; i < result.length; ++i) { - {{{ makeSetValue('p', 'i', 'result[i] != 0', 'i8') }}}; - } - } else if (result instanceof WebGLBuffer || - result instanceof WebGLProgram || - result instanceof WebGLFramebuffer || - result instanceof WebGLRenderbuffer || - result instanceof WebGLTexture) { - {{{ makeSetValue('p', '0', '1', 'i8') }}}; // non-zero ID is always 1! - } else { - GL.recordError(0x0500/*GL_INVALID_ENUM*/); -#if GL_ASSERTIONS - Module.printErr('GL_INVALID_ENUM in glGetBooleanv: Unknown object returned from WebGL getParameter(' + name_ + ')!'); -#endif - return; - } - break; - default: - GL.recordError(0x0500/*GL_INVALID_ENUM*/); -#if GL_ASSERTIONS - Module.printErr('GL_INVALID_ENUM in glGetBooleanv: Native code calling glGetBooleanv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!'); -#endif - return; - } + return GL.get(name_, p, 'Boolean'); }, glGenTextures__sig: 'vii', @@ -1808,7 +1735,7 @@ var LibraryGL = { // Add some emulation workarounds Module.printErr('WARNING: using emscripten GL emulation. This is a collection of limited workarounds, do not expect it to work.'); -#if GL_UNSAFE_OPTS == 0 +#if GL_UNSAFE_OPTS == 1 Module.printErr('WARNING: using emscripten GL emulation unsafe opts. If weirdness happens, try -s GL_UNSAFE_OPTS=0'); #endif @@ -2149,7 +2076,10 @@ var LibraryGL = { } } #endif - GL.currProgram = program; + if (GL.currProgram != program) { + GL.currentRenderer = null; // This changes the FFP emulation shader program, need to recompute that. + GL.currProgram = program; + } glUseProgram(program); } @@ -2689,32 +2619,85 @@ var LibraryGL = { GL_SRC_ALPHA ]; - this.traverseState = function CTexEnv_traverseState(keyView) { - keyView.next(this.mode); - keyView.next(this.colorCombiner); - keyView.next(this.alphaCombiner); - keyView.next(this.colorCombiner); - keyView.next(this.alphaScale); - keyView.next(this.envColor[0]); - keyView.next(this.envColor[1]); - keyView.next(this.envColor[2]); - keyView.next(this.envColor[3]); - - keyView.next(this.colorSrc[0]); - keyView.next(this.colorSrc[1]); - keyView.next(this.colorSrc[2]); - - keyView.next(this.alphaSrc[0]); - keyView.next(this.alphaSrc[1]); - keyView.next(this.alphaSrc[2]); - - keyView.next(this.colorOp[0]); - keyView.next(this.colorOp[1]); - keyView.next(this.colorOp[2]); - - keyView.next(this.alphaOp[0]); - keyView.next(this.alphaOp[1]); - keyView.next(this.alphaOp[2]); + // Map GLenums to small values to efficiently pack the enums to bits for tighter access. + this.traverseKey = { + // mode + 0x1E01 /* GL_REPLACE */: 0, + 0x2100 /* GL_MODULATE */: 1, + 0x0104 /* GL_ADD */: 2, + 0x0BE2 /* GL_BLEND */: 3, + 0x2101 /* GL_DECAL */: 4, + 0x8570 /* GL_COMBINE */: 5, + + // additional color and alpha combiners + 0x84E7 /* GL_SUBTRACT */: 3, + 0x8575 /* GL_INTERPOLATE */: 4, + + // color and alpha src + 0x1702 /* GL_TEXTURE */: 0, + 0x8576 /* GL_CONSTANT */: 1, + 0x8577 /* GL_PRIMARY_COLOR */: 2, + 0x8578 /* GL_PREVIOUS */: 3, + + // color and alpha op + 0x0300 /* GL_SRC_COLOR */: 0, + 0x0301 /* GL_ONE_MINUS_SRC_COLOR */: 1, + 0x0302 /* GL_SRC_ALPHA */: 2, + 0x0300 /* GL_ONE_MINUS_SRC_ALPHA */: 3 + }; + + // The tuple (key0,key1,key2) uniquely identifies the state of the variables in CTexEnv. + // -1 on key0 denotes 'the whole cached key is dirty' + this.key0 = -1; + this.key1 = 0; + this.key2 = 0; + + this.computeKey0 = function() { + var k = this.traverseKey; + var key = k[this.mode] * 1638400; // 6 distinct values. + key += k[this.colorCombiner] * 327680; // 5 distinct values. + key += k[this.alphaCombiner] * 65536; // 5 distinct values. + // The above three fields have 6*5*5=150 distinct values -> 8 bits. + key += (this.colorScale-1) * 16384; // 10 bits used. + key += (this.alphaScale-1) * 4096; // 12 bits used. + key += k[this.colorSrc[0]] * 1024; // 14 + key += k[this.colorSrc[1]] * 256; // 16 + key += k[this.colorSrc[2]] * 64; // 18 + key += k[this.alphaSrc[0]] * 16; // 20 + key += k[this.alphaSrc[1]] * 4; // 22 + key += k[this.alphaSrc[2]]; // 24 bits used total. + return key; + } + this.computeKey1 = function() { + var k = this.traverseKey; + key = k[this.colorOp[0]] * 4096; + key += k[this.colorOp[1]] * 1024; + key += k[this.colorOp[2]] * 256; + key += k[this.alphaOp[0]] * 16; + key += k[this.alphaOp[1]] * 4; + key += k[this.alphaOp[2]]; + return key; + } + // TODO: remove this. The color should not be part of the key! + this.computeKey2 = function() { + return this.envColor[0] * 16777216 + this.envColor[1] * 65536 + this.envColor[2] * 256 + 1 + this.envColor[3]; + } + this.recomputeKey = function() { + this.key0 = this.computeKey0(); + this.key1 = this.computeKey1(); + this.key2 = this.computeKey2(); + } + this.invalidateKey = function() { + this.key0 = -1; // The key of this texture unit must be recomputed when rendering the next time. + GL.immediate.currentRenderer = null; // The currently used renderer must be re-evaluated at next render. + } + this.traverseState = function(keyView) { + if (this.key0 == -1) { + this.recomputeKey(); + } + keyView.next(this.key0); + keyView.next(this.key1); + keyView.next(this.key2); }; } @@ -3076,16 +3059,28 @@ var LibraryGL = { var cur = getCurTexUnit(); switch (cap) { case GL_TEXTURE_1D: - cur.enabled_tex1D = true; + if (!cur.enabled_tex1D) { + GL.immediate.currentRenderer = null; // Renderer state changed, and must be recreated or looked up again. + cur.enabled_tex1D = true; + } break; case GL_TEXTURE_2D: - cur.enabled_tex2D = true; + if (!cur.enabled_tex2D) { + GL.immediate.currentRenderer = null; + cur.enabled_tex2D = true; + } break; case GL_TEXTURE_3D: - cur.enabled_tex3D = true; + if (!cur.enabled_tex3D) { + GL.immediate.currentRenderer = null; + cur.enabled_tex3D = true; + } break; case GL_TEXTURE_CUBE_MAP: - cur.enabled_texCube = true; + if (!cur.enabled_texCube) { + GL.immediate.currentRenderer = null; + cur.enabled_texCube = true; + } break; } }, @@ -3094,16 +3089,28 @@ var LibraryGL = { var cur = getCurTexUnit(); switch (cap) { case GL_TEXTURE_1D: - cur.enabled_tex1D = false; + if (cur.enabled_tex1D) { + GL.immediate.currentRenderer = null; // Renderer state changed, and must be recreated or looked up again. + cur.enabled_tex1D = false; + } break; case GL_TEXTURE_2D: - cur.enabled_tex2D = false; + if (cur.enabled_tex2D) { + GL.immediate.currentRenderer = null; + cur.enabled_tex2D = false; + } break; case GL_TEXTURE_3D: - cur.enabled_tex3D = false; + if (cur.enabled_tex3D) { + GL.immediate.currentRenderer = null; + cur.enabled_tex3D = false; + } break; case GL_TEXTURE_CUBE_MAP: - cur.enabled_texCube = false; + if (cur.enabled_texCube) { + GL.immediate.currentRenderer = null; + cur.enabled_texCube = false; + } break; } }, @@ -3115,10 +3122,16 @@ var LibraryGL = { var env = getCurTexUnit().env; switch (pname) { case GL_RGB_SCALE: - env.colorScale = param; + if (env.colorScale != param) { + env.invalidateKey(); // We changed FFP emulation renderer state. + env.colorScale = param; + } break; case GL_ALPHA_SCALE: - env.alphaScale = param; + if (env.alphaScale != param) { + env.invalidateKey(); + env.alphaScale = param; + } break; default: @@ -3133,61 +3146,112 @@ var LibraryGL = { var env = getCurTexUnit().env; switch (pname) { case GL_TEXTURE_ENV_MODE: - env.mode = param; + if (env.mode != param) { + env.invalidateKey(); // We changed FFP emulation renderer state. + env.mode = param; + } break; case GL_COMBINE_RGB: - env.colorCombiner = param; + if (env.colorCombiner != param) { + env.invalidateKey(); + env.colorCombiner = param; + } break; case GL_COMBINE_ALPHA: - env.alphaCombiner = param; + if (env.alphaCombiner != param) { + env.invalidateKey(); + env.alphaCombiner = param; + } break; case GL_SRC0_RGB: - env.colorSrc[0] = param; + if (env.colorSrc[0] != param) { + env.invalidateKey(); + env.colorSrc[0] = param; + } break; case GL_SRC1_RGB: - env.colorSrc[1] = param; + if (env.colorSrc[1] != param) { + env.invalidateKey(); + env.colorSrc[1] = param; + } break; case GL_SRC2_RGB: - env.colorSrc[2] = param; + if (env.colorSrc[2] != param) { + env.invalidateKey(); + env.colorSrc[2] = param; + } break; case GL_SRC0_ALPHA: - env.alphaSrc[0] = param; + if (env.alphaSrc[0] != param) { + env.invalidateKey(); + env.alphaSrc[0] = param; + } break; case GL_SRC1_ALPHA: - env.alphaSrc[1] = param; + if (env.alphaSrc[1] != param) { + env.invalidateKey(); + env.alphaSrc[1] = param; + } break; case GL_SRC2_ALPHA: - env.alphaSrc[2] = param; + if (env.alphaSrc[2] != param) { + env.invalidateKey(); + env.alphaSrc[2] = param; + } break; case GL_OPERAND0_RGB: - env.colorOp[0] = param; + if (env.colorOp[0] != param) { + env.invalidateKey(); + env.colorOp[0] = param; + } break; case GL_OPERAND1_RGB: - env.colorOp[1] = param; + if (env.colorOp[1] != param) { + env.invalidateKey(); + env.colorOp[1] = param; + } break; case GL_OPERAND2_RGB: - env.colorOp[2] = param; + if (env.colorOp[2] != param) { + env.invalidateKey(); + env.colorOp[2] = param; + } break; case GL_OPERAND0_ALPHA: - env.alphaOp[0] = param; + if (env.alphaOp[0] != param) { + env.invalidateKey(); + env.alphaOp[0] = param; + } break; case GL_OPERAND1_ALPHA: - env.alphaOp[1] = param; + if (env.alphaOp[1] != param) { + env.invalidateKey(); + env.alphaOp[1] = param; + } break; case GL_OPERAND2_ALPHA: - env.alphaOp[2] = param; + if (env.alphaOp[2] != param) { + env.invalidateKey(); + env.alphaOp[2] = param; + } break; case GL_RGB_SCALE: - env.colorScale = param; + if (env.colorScale != param) { + env.invalidateKey(); + env.colorScale = param; + } break; case GL_ALPHA_SCALE: - env.alphaScale = param; + if (env.alphaScale != param) { + env.invalidateKey(); + env.alphaScale = param; + } break; default: @@ -3203,7 +3267,10 @@ var LibraryGL = { case GL_TEXTURE_ENV_COLOR: { for (var i = 0; i < 4; i++) { var param = {{{ makeGetValue('params', 'i*4', 'float') }}}; - env.envColor[i] = param; + if (env.envColor[i] != param) { + env.invalidateKey(); // We changed FFP emulation renderer state. + env.envColor[i] = param; + } } break } @@ -3243,26 +3310,21 @@ var LibraryGL = { NORMAL: 1, COLOR: 2, TEXTURE0: 3, - TEXTURE1: 4, - TEXTURE2: 5, - TEXTURE3: 6, - TEXTURE4: 7, - TEXTURE5: 8, - TEXTURE6: 9, - NUM_ATTRIBUTES: 10, // Overwritten in init(). - MAX_TEXTURES: 7, // Overwritten in init(). + NUM_ATTRIBUTES: -1, // Initialized in GL emulation init(). + MAX_TEXTURES: -1, // Initialized in GL emulation init(). totalEnabledClientAttributes: 0, enabledClientAttributes: [0, 0], clientAttributes: [], // raw data, including possible unneeded ones liveClientAttributes: [], // the ones actually alive in the current computation, sorted + currentRenderer: null, // Caches the currently active FFP emulation renderer, so that it does not have to be re-looked up unless relevant state changes. modifiedClientAttributes: false, clientActiveTexture: 0, clientColor: null, usedTexUnitList: [], fixedFunctionProgram: null, - setClientAttribute: function(name, size, type, stride, pointer) { + setClientAttribute: function setClientAttribute(name, size, type, stride, pointer) { var attrib = this.clientAttributes[name]; if (!attrib) { for (var i = 0; i <= name; i++) { // keep flat @@ -3289,7 +3351,7 @@ var LibraryGL = { }, // Renderers - addRendererComponent: function(name, size, type) { + addRendererComponent: function addRendererComponent(name, size, type) { if (!this.rendererComponents[name]) { this.rendererComponents[name] = 1; #if ASSERTIONS @@ -3305,13 +3367,18 @@ var LibraryGL = { } }, - disableBeginEndClientAttributes: function() { + disableBeginEndClientAttributes: function disableBeginEndClientAttributes() { for (var i = 0; i < this.NUM_ATTRIBUTES; i++) { if (this.rendererComponents[i]) this.enabledClientAttributes[i] = false; } }, - getRenderer: function() { + getRenderer: function getRenderer() { + // If no FFP state has changed that would have forced to re-evaluate which FFP emulation shader to use, + // we have the currently used renderer in cache, and can immediately return that. + if (this.currentRenderer) { + return this.currentRenderer; + } // return a renderer object given the liveClientAttributes // we maintain a cache of renderers, optimized to not generate garbage var attributes = GL.immediate.liveClientAttributes; @@ -3320,10 +3387,11 @@ var LibraryGL = { var keyView = cacheMap.getStaticKeyView().reset(); // By attrib state: + var enabledAttributesKey = 0; for (var i = 0; i < attributes.length; i++) { - var attribute = attributes[i]; - keyView.next(attribute.name).next(attribute.size).next(attribute.type); + enabledAttributesKey |= 1 << attributes[i].name; } + keyView.next(enabledAttributesKey); // By fog state: var fogParam = 0; @@ -3349,18 +3417,23 @@ var LibraryGL = { } // If we don't already have it, create it. - if (!keyView.get()) { + var renderer = keyView.get(); + if (!renderer) { #if GL_DEBUG Module.printErr('generating renderer for ' + JSON.stringify(attributes)); #endif - keyView.set(this.createRenderer()); + renderer = this.createRenderer(); + this.currentRenderer = renderer; + keyView.set(renderer); + return renderer; } - return keyView.get(); + this.currentRenderer = renderer; // Cache the currently used renderer, so later lookups without state changes can get this fast. + return renderer; }, - createRenderer: function(renderer) { + createRenderer: function createRenderer(renderer) { var useCurrProgram = !!GL.currProgram; - var hasTextures = false, textureSizes = [], textureTypes = []; + var hasTextures = false; for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { var texAttribName = GL.immediate.TEXTURE0 + i; if (!GL.immediate.enabledClientAttributes[texAttribName]) @@ -3374,24 +3447,11 @@ var LibraryGL = { } #endif - textureSizes[i] = GL.immediate.clientAttributes[texAttribName].size; - textureTypes[i] = GL.immediate.clientAttributes[texAttribName].type; hasTextures = true; } - var positionSize = GL.immediate.clientAttributes[GL.immediate.VERTEX].size; - var positionType = GL.immediate.clientAttributes[GL.immediate.VERTEX].type; - var colorSize = 0, colorType; - if (GL.immediate.enabledClientAttributes[GL.immediate.COLOR]) { - colorSize = GL.immediate.clientAttributes[GL.immediate.COLOR].size; - colorType = GL.immediate.clientAttributes[GL.immediate.COLOR].type; - } - var normalSize = 0, normalType; - if (GL.immediate.enabledClientAttributes[GL.immediate.NORMAL]) { - normalSize = GL.immediate.clientAttributes[GL.immediate.NORMAL].size; - normalType = GL.immediate.clientAttributes[GL.immediate.NORMAL].type; - } + var ret = { - init: function() { + init: function init() { // For fixed-function shader generation. var uTexUnitPrefix = 'u_texUnit'; var aTexCoordPrefix = 'a_texCoord'; @@ -3524,10 +3584,25 @@ var LibraryGL = { this.program = Module.ctx.createProgram(); Module.ctx.attachShader(this.program, this.vertexShader); Module.ctx.attachShader(this.program, this.fragmentShader); - Module.ctx.bindAttribLocation(this.program, 0, 'a_position'); + + // As optimization, bind all attributes to prespecified locations, so that the FFP emulation + // code can submit attributes to any generated FFP shader without having to examine each shader in turn. + // These prespecified locations are only assumed if GL_FFP_ONLY is specified, since user could also create their + // own shaders that didn't have attributes in the same locations. + Module.ctx.bindAttribLocation(this.program, GL.immediate.VERTEX, 'a_position'); + Module.ctx.bindAttribLocation(this.program, GL.immediate.COLOR, 'a_color'); + Module.ctx.bindAttribLocation(this.program, GL.immediate.NORMAL, 'a_normal'); + for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { + Module.ctx.bindAttribLocation(this.program, GL.immediate.TEXTURE0 + i, 'a_texCoord'+i); + Module.ctx.bindAttribLocation(this.program, GL.immediate.TEXTURE0 + i, aTexCoordPrefix+i); + } Module.ctx.linkProgram(this.program); } + // Stores a map that remembers which matrix uniforms are up-to-date in this FFP renderer, so they don't need to be resubmitted + // each time we render with this program. + this.textureMatrixVersion = {}; + this.positionLocation = Module.ctx.getAttribLocation(this.program, 'a_position'); this.texCoordLocations = []; @@ -3570,7 +3645,9 @@ var LibraryGL = { this.projectionLocation = Module.ctx.getUniformLocation(this.program, 'u_projection'); this.hasTextures = hasTextures; - this.hasNormal = normalSize > 0 && this.normalLocation >= 0; + this.hasNormal = GL.immediate.enabledClientAttributes[GL.immediate.NORMAL] && + GL.immediate.clientAttributes[GL.immediate.NORMAL].size > 0 && + this.normalLocation >= 0; this.hasColor = (this.colorLocation === 0) || this.colorLocation > 0; this.floatType = Module.ctx.FLOAT; // minor optimization @@ -3583,7 +3660,7 @@ var LibraryGL = { this.fogScaleLocation || this.fogDensityLocation); }, - prepare: function() { + prepare: function prepare() { // Calculate the array buffer var arrayBuffer; if (!GL.currArrayBuffer) { @@ -3598,10 +3675,10 @@ var LibraryGL = { arrayBuffer = GL.currArrayBuffer; } +#if GL_UNSAFE_OPTS // If the array buffer is unchanged and the renderer as well, then we can avoid all the work here // XXX We use some heuristics here, and this may not work in all cases. Try disabling GL_UNSAFE_OPTS if you // have odd glitches -#if GL_UNSAFE_OPTS var lastRenderer = GL.immediate.lastRenderer; var canSkip = this == lastRenderer && arrayBuffer == GL.immediate.lastArrayBuffer && @@ -3636,62 +3713,105 @@ var LibraryGL = { GL.immediate.fixedFunctionProgram = this.program; } - if (this.modelViewLocation) Module.ctx.uniformMatrix4fv(this.modelViewLocation, false, GL.immediate.matrix['m']); - if (this.projectionLocation) Module.ctx.uniformMatrix4fv(this.projectionLocation, false, GL.immediate.matrix['p']); + if (this.modelViewLocation && this.modelViewMatrixVersion != GL.immediate.matrixVersion['m']) { + this.modelViewMatrixVersion = GL.immediate.matrixVersion['m']; + Module.ctx.uniformMatrix4fv(this.modelViewLocation, false, GL.immediate.matrix['m']); + } + if (this.projectionLocation && this.projectionMatrixVersion != GL.immediate.matrixVersion['p']) { + this.projectionMatrixVersion = GL.immediate.matrixVersion['p']; + Module.ctx.uniformMatrix4fv(this.projectionLocation, false, GL.immediate.matrix['p']); + } var clientAttributes = GL.immediate.clientAttributes; + var posAttr = clientAttributes[GL.immediate.VERTEX]; #if GL_ASSERTIONS - GL.validateVertexAttribPointer(positionSize, positionType, GL.immediate.stride, clientAttributes[GL.immediate.VERTEX].offset); + GL.validateVertexAttribPointer(posAttr.size, posAttr.type, GL.immediate.stride, clientAttributes[GL.immediate.VERTEX].offset); #endif - Module.ctx.vertexAttribPointer(this.positionLocation, positionSize, positionType, false, - GL.immediate.stride, clientAttributes[GL.immediate.VERTEX].offset); + +#if GL_FFP_ONLY + if (!GL.currArrayBuffer) { + Module.ctx.vertexAttribPointer(GL.immediate.VERTEX, posAttr.size, posAttr.type, false, GL.immediate.stride, posAttr.offset); + GL.enableVertexAttribArray(GL.immediate.VERTEX); + if (this.hasNormal) { + var normalAttr = clientAttributes[GL.immediate.NORMAL]; + Module.ctx.vertexAttribPointer(GL.immediate.NORMAL, normalAttr.size, normalAttr.type, true, GL.immediate.stride, normalAttr.offset); + GL.enableVertexAttribArray(GL.immediate.NORMAL); + } + } +#else + Module.ctx.vertexAttribPointer(this.positionLocation, posAttr.size, posAttr.type, false, GL.immediate.stride, posAttr.offset); Module.ctx.enableVertexAttribArray(this.positionLocation); + if (this.hasNormal) { + var normalAttr = clientAttributes[GL.immediate.NORMAL]; +#if GL_ASSERTIONS + GL.validateVertexAttribPointer(normalAttr.size, normalAttr.type, GL.immediate.stride, normalAttr.offset); +#endif + Module.ctx.vertexAttribPointer(this.normalLocation, normalAttr.size, normalAttr.type, true, GL.immediate.stride, normalAttr.offset); + Module.ctx.enableVertexAttribArray(this.normalLocation); + } +#endif if (this.hasTextures) { - //for (var i = 0; i < this.usedTexUnitList.length; i++) { - // var texUnitID = this.usedTexUnitList[i]; for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { - var texUnitID = i; - var attribLoc = this.texCoordLocations[texUnitID]; +#if GL_FFP_ONLY + if (!GL.currArrayBuffer) { + var attribLoc = GL.immediate.TEXTURE0+i; + var texAttr = clientAttributes[attribLoc]; + if (texAttr.size) { + Module.ctx.vertexAttribPointer(attribLoc, texAttr.size, texAttr.type, false, GL.immediate.stride, texAttr.offset); + GL.enableVertexAttribArray(attribLoc); + } else { + // These two might be dangerous, but let's try them. + Module.ctx.vertexAttrib4f(attribLoc, 0, 0, 0, 1); + GL.disableVertexAttribArray(attribLoc); + } + } +#else + var attribLoc = this.texCoordLocations[i]; if (attribLoc === undefined || attribLoc < 0) continue; + var texAttr = clientAttributes[GL.immediate.TEXTURE0+i]; - if (texUnitID < textureSizes.length && textureSizes[texUnitID]) { + if (texAttr.size) { #if GL_ASSERTIONS - GL.validateVertexAttribPointer(textureSizes[texUnitID], textureTypes[texUnitID], GL.immediate.stride, GL.immediate.clientAttributes[GL.immediate.TEXTURE0 + texUnitID].offset); + GL.validateVertexAttribPointer(texAttr.size, texAttr.type, GL.immediate.stride, texAttr.offset); #endif - Module.ctx.vertexAttribPointer(attribLoc, textureSizes[texUnitID], textureTypes[texUnitID], false, - GL.immediate.stride, GL.immediate.clientAttributes[GL.immediate.TEXTURE0 + texUnitID].offset); + Module.ctx.vertexAttribPointer(attribLoc, texAttr.size, texAttr.type, false, GL.immediate.stride, texAttr.offset); Module.ctx.enableVertexAttribArray(attribLoc); } else { // These two might be dangerous, but let's try them. Module.ctx.vertexAttrib4f(attribLoc, 0, 0, 0, 1); Module.ctx.disableVertexAttribArray(attribLoc); } - } - for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { - if (this.textureMatrixLocations[i]) { // XXX might we need this even without the condition we are currently in? - Module.ctx.uniformMatrix4fv(this.textureMatrixLocations[i], false, GL.immediate.matrix['t' + i]); +#endif + var t = 't'+i; + if (this.textureMatrixLocations[i] && this.textureMatrixVersion[t] != GL.immediate.matrixVersion[t]) { // XXX might we need this even without the condition we are currently in? + this.textureMatrixVersion[t] = GL.immediate.matrixVersion[t]; + Module.ctx.uniformMatrix4fv(this.textureMatrixLocations[i], false, GL.immediate.matrix[t]); } } } - if (colorSize) { + if (GL.immediate.enabledClientAttributes[GL.immediate.COLOR]) { + var colorAttr = clientAttributes[GL.immediate.COLOR]; #if GL_ASSERTIONS - GL.validateVertexAttribPointer(colorSize, colorType, GL.immediate.stride, clientAttributes[GL.immediate.COLOR].offset); + GL.validateVertexAttribPointer(colorAttr.size, colorAttr.type, GL.immediate.stride, colorAttr.offset); #endif - Module.ctx.vertexAttribPointer(this.colorLocation, colorSize, colorType, true, - GL.immediate.stride, clientAttributes[GL.immediate.COLOR].offset); +#if GL_FFP_ONLY + if (!GL.currArrayBuffer) { + Module.ctx.vertexAttribPointer(GL.immediate.COLOR, colorAttr.size, colorAttr.type, true, GL.immediate.stride, colorAttr.offset); + GL.enableVertexAttribArray(GL.immediate.COLOR); + } +#else + Module.ctx.vertexAttribPointer(this.colorLocation, colorAttr.size, colorAttr.type, true, GL.immediate.stride, colorAttr.offset); Module.ctx.enableVertexAttribArray(this.colorLocation); +#endif } else if (this.hasColor) { +#if GL_FFP_ONLY + GL.disableVertexAttribArray(GL.immediate.COLOR); + Module.ctx.vertexAttrib4fv(GL.immediate.COLOR, GL.immediate.clientColor); +#else Module.ctx.disableVertexAttribArray(this.colorLocation); Module.ctx.vertexAttrib4fv(this.colorLocation, GL.immediate.clientColor); - } - if (this.hasNormal) { -#if GL_ASSERTIONS - GL.validateVertexAttribPointer(normalSize, normalType, GL.immediate.stride, clientAttributes[GL.immediate.NORMAL].offset); #endif - Module.ctx.vertexAttribPointer(this.normalLocation, normalSize, normalType, true, - GL.immediate.stride, clientAttributes[GL.immediate.NORMAL].offset); - Module.ctx.enableVertexAttribArray(this.normalLocation); } if (this.hasFog) { if (this.fogColorLocation) Module.ctx.uniform4fv(this.fogColorLocation, GLEmulation.fogColor); @@ -3701,11 +3821,12 @@ var LibraryGL = { } }, - cleanup: function() { + cleanup: function cleanup() { +#if !GL_FFP_ONLY Module.ctx.disableVertexAttribArray(this.positionLocation); if (this.hasTextures) { - for (var i = 0; i < textureSizes.length; i++) { - if (textureSizes[i] && this.texCoordLocations[i] >= 0) { + for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { + if (GL.immediate.enabledClientAttributes[GL.immediate.TEXTURE0+i] && this.texCoordLocations[i] >= 0) { Module.ctx.disableVertexAttribArray(this.texCoordLocations[i]); } } @@ -3729,6 +3850,7 @@ var LibraryGL = { GL.immediate.lastProgram = null; #endif GL.immediate.matricesModified = true; +#endif } }; ret.init(); @@ -3858,11 +3980,15 @@ var LibraryGL = { this.TexEnvJIT.init(Module.ctx); - GL.immediate.MAX_TEXTURES = Module.ctx.getParameter(Module.ctx.MAX_TEXTURE_IMAGE_UNITS); - GL.immediate.NUM_ATTRIBUTES = GL.immediate.TEXTURE0 + GL.immediate.MAX_TEXTURES; + // User can override the maximum number of texture units that we emulate. Using fewer texture units increases runtime performance + // slightly, so it is advantageous to choose as small value as needed. + GL.immediate.MAX_TEXTURES = Module['GL_MAX_TEXTURE_IMAGE_UNITS'] || Module.ctx.getParameter(Module.ctx.MAX_TEXTURE_IMAGE_UNITS); + GL.immediate.NUM_ATTRIBUTES = 3 /*pos+normal+color attributes*/ + GL.immediate.MAX_TEXTURES; GL.immediate.clientAttributes = []; + GLEmulation.enabledClientAttribIndices = []; for (var i = 0; i < GL.immediate.NUM_ATTRIBUTES; i++) { GL.immediate.clientAttributes.push({}); + GLEmulation.enabledClientAttribIndices.push(false); } this.matrixStack['m'] = []; @@ -3872,13 +3998,18 @@ var LibraryGL = { } // Initialize matrix library - + // When user sets a matrix, increment a 'version number' on the new data, and when rendering, submit + // the matrices to the shader program only if they have an old version of the data. + GL.immediate.matrixVersion = {}; GL.immediate.matrix['m'] = GL.immediate.matrix.lib.mat4.create(); + GL.immediate.matrixVersion['m'] = 0; GL.immediate.matrix.lib.mat4.identity(GL.immediate.matrix['m']); GL.immediate.matrix['p'] = GL.immediate.matrix.lib.mat4.create(); + GL.immediate.matrixVersion['p'] = 0; GL.immediate.matrix.lib.mat4.identity(GL.immediate.matrix['p']); for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { GL.immediate.matrix['t' + i] = GL.immediate.matrix.lib.mat4.create(); + GL.immediate.matrixVersion['t' + i] = 0; } // Renderer cache @@ -3899,7 +4030,7 @@ var LibraryGL = { // Modifies liveClientAttributes, stride, vertexPointer, vertexCounter // count: number of elements we will draw // beginEnd: whether we are drawing the results of a begin/end block - prepareClientAttributes: function(count, beginEnd) { + prepareClientAttributes: function prepareClientAttributes(count, beginEnd) { // If no client attributes were modified since we were last called, do nothing. Note that this // does not work for glBegin/End, where we generate renderer components dynamically and then // disable them ourselves, but it does help with glDrawElements/Arrays. @@ -3997,7 +4128,7 @@ var LibraryGL = { } }, - flush: function(numProvidedIndexes, startIndex, ptr) { + flush: function flush(numProvidedIndexes, startIndex, ptr) { #if ASSERTIONS assert(numProvidedIndexes >= 0 || !numProvidedIndexes); #endif @@ -4070,7 +4201,7 @@ var LibraryGL = { Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, GL.buffers[GL.currElementArrayBuffer] || null); } -#if GL_UNSAFE_OPTS == 0 +#if GL_UNSAFE_OPTS == 0 && !GL_FFP_ONLY renderer.cleanup(); #endif } @@ -4237,7 +4368,7 @@ var LibraryGL = { glColor4ubv__deps: ['glColor4ub'], glColor4ubv: function(p) { _glColor4ub({{{ makeGetValue('p', '0', 'i8') }}}, {{{ makeGetValue('p', '1', 'i8') }}}, {{{ makeGetValue('p', '2', 'i8') }}}, {{{ makeGetValue('p', '3', 'i8') }}}); - }, + }, glFogf: function(pname, param) { // partial support, TODO switch(pname) { @@ -4318,10 +4449,12 @@ var LibraryGL = { if (disable && GL.immediate.enabledClientAttributes[attrib]) { GL.immediate.enabledClientAttributes[attrib] = false; GL.immediate.totalEnabledClientAttributes--; + this.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed. if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledClientStates[cap]; } else if (!disable && !GL.immediate.enabledClientAttributes[attrib]) { GL.immediate.enabledClientAttributes[attrib] = true; GL.immediate.totalEnabledClientAttributes++; + this.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed. if (GLEmulation.currentVao) GLEmulation.currentVao.enabledClientStates[cap] = 1; } GL.immediate.modifiedClientAttributes = true; @@ -4333,15 +4466,40 @@ var LibraryGL = { glVertexPointer__deps: ['$GLEmulation'], // if any pointers are used, glVertexPointer must be, and if it is, then we need emulation glVertexPointer: function(size, type, stride, pointer) { GL.immediate.setClientAttribute(GL.immediate.VERTEX, size, type, stride, pointer); +#if GL_FFP_ONLY + if (GL.currArrayBuffer) { + Module.ctx.vertexAttribPointer(GL.immediate.VERTEX, size, type, false, stride, pointer); + GL.enableVertexAttribArray(GL.immediate.VERTEX); + } +#endif }, glTexCoordPointer: function(size, type, stride, pointer) { GL.immediate.setClientAttribute(GL.immediate.TEXTURE0 + GL.immediate.clientActiveTexture, size, type, stride, pointer); +#if GL_FFP_ONLY + if (GL.currArrayBuffer) { + var loc = GL.immediate.TEXTURE0 + GL.immediate.clientActiveTexture; + Module.ctx.vertexAttribPointer(loc, size, type, false, stride, pointer); + GL.enableVertexAttribArray(loc); + } +#endif }, glNormalPointer: function(type, stride, pointer) { GL.immediate.setClientAttribute(GL.immediate.NORMAL, 3, type, stride, pointer); +#if GL_FFP_ONLY + if (GL.currArrayBuffer) { + Module.ctx.vertexAttribPointer(GL.immediate.NORMAL, size, type, true, stride, pointer); + GL.enableVertexAttribArray(GL.immediate.NORMAL); + } +#endif }, glColorPointer: function(size, type, stride, pointer) { GL.immediate.setClientAttribute(GL.immediate.COLOR, size, type, stride, pointer); +#if GL_FFP_ONLY + if (GL.currArrayBuffer) { + Module.ctx.vertexAttribPointer(GL.immediate.COLOR, size, type, true, stride, pointer); + GL.enableVertexAttribArray(GL.immediate.COLOR); + } +#endif }, glClientActiveTexture__sig: 'vi', @@ -4424,23 +4582,27 @@ var LibraryGL = { glPushMatrix: function() { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrixStack[GL.immediate.currentMatrix].push( Array.prototype.slice.call(GL.immediate.matrix[GL.immediate.currentMatrix])); }, glPopMatrix: function() { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrix[GL.immediate.currentMatrix] = GL.immediate.matrixStack[GL.immediate.currentMatrix].pop(); }, glLoadIdentity__deps: ['$GL', '$GLImmediateSetup'], glLoadIdentity: function() { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrix.lib.mat4.identity(GL.immediate.matrix[GL.immediate.currentMatrix]); }, glLoadMatrixd: function(matrix) { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrix.lib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, GL.immediate.matrix[GL.immediate.currentMatrix]); }, @@ -4449,35 +4611,41 @@ var LibraryGL = { if (GL.debug) Module.printErr('glLoadMatrixf receiving: ' + Array.prototype.slice.call(HEAPF32.subarray(matrix >> 2, (matrix >> 2) + 16))); #endif GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrix.lib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}, GL.immediate.matrix[GL.immediate.currentMatrix]); }, glLoadTransposeMatrixd: function(matrix) { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrix.lib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, GL.immediate.matrix[GL.immediate.currentMatrix]); GL.immediate.matrix.lib.mat4.transpose(GL.immediate.matrix[GL.immediate.currentMatrix]); }, glLoadTransposeMatrixf: function(matrix) { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrix.lib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}, GL.immediate.matrix[GL.immediate.currentMatrix]); GL.immediate.matrix.lib.mat4.transpose(GL.immediate.matrix[GL.immediate.currentMatrix]); }, glMultMatrixd: function(matrix) { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix], {{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}); }, glMultMatrixf: function(matrix) { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix], {{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}); }, glMultTransposeMatrixd: function(matrix) { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; var colMajor = GL.immediate.matrix.lib.mat4.create(); GL.immediate.matrix.lib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, colMajor); GL.immediate.matrix.lib.mat4.transpose(colMajor); @@ -4486,6 +4654,7 @@ var LibraryGL = { glMultTransposeMatrixf: function(matrix) { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; var colMajor = GL.immediate.matrix.lib.mat4.create(); GL.immediate.matrix.lib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}, colMajor); GL.immediate.matrix.lib.mat4.transpose(colMajor); @@ -4494,6 +4663,7 @@ var LibraryGL = { glFrustum: function(left, right, bottom, top_, nearVal, farVal) { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix], GL.immediate.matrix.lib.mat4.frustum(left, right, bottom, top_, nearVal, farVal)); }, @@ -4501,6 +4671,7 @@ var LibraryGL = { glOrtho: function(left, right, bottom, top_, nearVal, farVal) { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix], GL.immediate.matrix.lib.mat4.ortho(left, right, bottom, top_, nearVal, farVal)); }, @@ -4508,18 +4679,21 @@ var LibraryGL = { glScaled: function(x, y, z) { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrix.lib.mat4.scale(GL.immediate.matrix[GL.immediate.currentMatrix], [x, y, z]); }, glScalef: 'glScaled', glTranslated: function(x, y, z) { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrix.lib.mat4.translate(GL.immediate.matrix[GL.immediate.currentMatrix], [x, y, z]); }, glTranslatef: 'glTranslated', glRotated: function(angle, x, y, z) { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrix.lib.mat4.rotate(GL.immediate.matrix[GL.immediate.currentMatrix], angle*Math.PI/180, [x, y, z]); }, glRotatef: 'glRotated', @@ -4602,6 +4776,7 @@ var LibraryGL = { gluPerspective: function(fov, aspect, near, far) { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrix[GL.immediate.currentMatrix] = GL.immediate.matrix.lib.mat4.perspective(fov, aspect, near, far, GL.immediate.matrix[GL.immediate.currentMatrix]); @@ -4609,6 +4784,7 @@ var LibraryGL = { gluLookAt: function(ex, ey, ez, cx, cy, cz, ux, uy, uz) { GL.immediate.matricesModified = true; + GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0; GL.immediate.matrix.lib.mat4.lookAt(GL.immediate.matrix[GL.immediate.currentMatrix], [ex, ey, ez], [cx, cy, cz], [ux, uy, uz]); }, @@ -4817,14 +4993,14 @@ var LibraryGL = { glClearColor__sig: 'viiii', glIsEnabled__sig: 'ii', glFrontFace__sig: 'vi', - glSampleCoverage__sig: 'vi', + glSampleCoverage__sig: 'vii', }; // Simple pass-through functions. Starred ones have return values. [X] ones have X in the C name but not in the JS name [[0, 'finish flush'], - [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation sampleCoverage isEnabled*'], - [2, 'blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate hint polygonOffset vertexAttrib1f'], + [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation isEnabled*'], + [2, 'blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate hint polygonOffset vertexAttrib1f sampleCoverage'], [3, 'texParameteri texParameterf vertexAttrib2f stencilFunc stencilOp'], [4, 'viewport clearColor scissor vertexAttrib3f colorMask renderbufferStorage blendFuncSeparate blendColor stencilFuncSeparate stencilOpSeparate'], [5, 'vertexAttrib4f'], diff --git a/src/library_glfw.js b/src/library_glfw.js index 647d4bb622be1..17e8956a46235 100644 --- a/src/library_glfw.js +++ b/src/library_glfw.js @@ -120,7 +120,6 @@ var LibraryGLFW = { if (event.charCode) { var char = GLFW.getUnicodeChar(event.charCode); if (char !== null && GLFW.charFunc) { - event.preventDefault(); Runtime.dynCall('vii', GLFW.charFunc, [event.charCode, 1]); } } @@ -130,13 +129,18 @@ var LibraryGLFW = { var key = GLFW.DOMToGLFWKeyCode(event.keyCode); if (key && GLFW.keyFunc) { GLFW.keys[key] = status; - event.preventDefault(); Runtime.dynCall('vii', GLFW.keyFunc, [key, status]); } }, onKeydown: function(event) { GLFW.onKeyChanged(event, 1);//GLFW_PRESS + // This logic comes directly from the sdl implementation. We cannot + // call preventDefault on all keydown events otherwise onKeyPress will + // not get called + if (event.keyCode === 8 /* backspace */ || event.keyCode === 9 /* tab */) { + event.preventDefault(); + } }, onKeyup: function(event) { diff --git a/src/library_sdl.js b/src/library_sdl.js index eb8eea9775d90..2efc127123866 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -715,7 +715,7 @@ var LibrarySDL = { // Joystick helper methods and state - joystickEventState: 0, + joystickEventState: 1, // SDL_ENABLE lastJoystickState: {}, // Map from SDL_Joystick* to their last known state. Required to determine if a change has occurred. // Maps Joystick names to pointers. Allows us to avoid reallocating memory for // joystick names each time this function is called. @@ -1247,6 +1247,8 @@ var LibrarySDL = { return 0; }, + SDL_LowerBlit: 'SDL_UpperBlit', + SDL_FillRect: function(surf, rect, color) { var surfData = SDL.surfaces[surf]; assert(!surfData.locked); // but we could unlock and re-lock if we must.. @@ -1910,23 +1912,19 @@ var LibrarySDL = { var filename = ''; var audio; var bytes; - + if (rwops.filename !== undefined) { filename = PATH.resolve(rwops.filename); var raw = Module["preloadedAudios"][filename]; if (!raw) { if (raw === null) Module.printErr('Trying to reuse preloaded audio, but freePreloadedMediaOnUse is set!'); Runtime.warnOnce('Cannot find preloaded audio ' + filename); - + // see if we can read the file-contents from the in-memory FS - var fileObject = FS.findObject(filename); - - if (fileObject === null) Module.printErr('Couldn\'t find file for: ' + filename); - - // We found the file. Load the contents - if (fileObject && !fileObject.isFolder && fileObject.read) { - bytes = fileObject.contents; - } else { + try { + bytes = FS.readFile(filename); + } catch (e) { + Module.printErr('Couldn\'t find file for: ' + filename); return 0; } } @@ -1941,16 +1939,16 @@ var LibrarySDL = { else { return 0; } - + // Here, we didn't find a preloaded audio but we either were passed a filepath for // which we loaded bytes, or we were passed some bytes if (audio === undefined && bytes) { - var blob = new Blob([new Uint8Array(bytes)], {type: rwops.mimetype}); + var blob = new Blob([bytes], {type: rwops.mimetype}); var url = URL.createObjectURL(blob); audio = new Audio(); audio.src = url; } - + var id = SDL.audios.length; // Keep the loaded audio in the audio arrays, ready for playback SDL.audios.push({ diff --git a/src/library_sockfs.js b/src/library_sockfs.js index bc3aa99743dc1..2028d841de852 100644 --- a/src/library_sockfs.js +++ b/src/library_sockfs.js @@ -1,6 +1,6 @@ mergeInto(LibraryManager.library, { $SOCKFS__postset: '__ATINIT__.push({ func: function() { SOCKFS.root = FS.mount(SOCKFS, {}, null); } });', - $SOCKFS__deps: ['$FS'], + $SOCKFS__deps: ['$FS', 'mkport'], $SOCKFS: { mount: function(mount) { return FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); diff --git a/src/modules.js b/src/modules.js index 5d48ede2819db..e2d3433f2e13a 100644 --- a/src/modules.js +++ b/src/modules.js @@ -282,7 +282,12 @@ var Functions = { sig += Functions.getSignatureLetter(type); } else { var chunks = getNumIntChunks(type); - for (var j = 0; j < chunks; j++) sig += 'i'; + if (chunks > 0) { + for (var j = 0; j < chunks; j++) sig += 'i'; + } else if (type !== '...') { + // some special type like a SIMD vector (anything but varargs, which we handle below) + sig += Functions.getSignatureLetter(type); + } } } if (hasVarArgs) sig += 'i'; @@ -424,6 +429,26 @@ var LibraryManager = { eval(processMacros(preprocess(read(libraries[i])))); } + /* + // export code for CallHandlers.h + printErr('============================'); + for (var x in this.library) { + var y = this.library[x]; + if (typeof y === 'string' && x.indexOf('__sig') < 0 && x.indexOf('__postset') < 0 && y.indexOf(' ') < 0) { + printErr('DEF_REDIRECT_HANDLER(' + x + ', ' + y + ');'); + } + } + printErr('============================'); + for (var x in this.library) { + var y = this.library[x]; + if (typeof y === 'string' && x.indexOf('__sig') < 0 && x.indexOf('__postset') < 0 && y.indexOf(' ') < 0) { + printErr(' SETUP_CALL_HANDLER(' + x + ');'); + } + } + printErr('============================'); + // end export code for CallHandlers.h + */ + this.loaded = true; }, @@ -483,6 +508,11 @@ var PassManager = { print('\n//FORWARDED_DATA:' + JSON.stringify({ Functions: { tables: Functions.tables } })); + } else if (phase == 'glue') { + print('\n//FORWARDED_DATA:' + JSON.stringify({ + Functions: Functions, + EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS + })); } }, load: function(json) { @@ -496,6 +526,7 @@ var PassManager = { for (var i in data.Functions) { Functions[i] = data.Functions[i]; } + EXPORTED_FUNCTIONS = data.EXPORTED_FUNCTIONS; /* print('\n//LOADED_DATA:' + phase + ':' + JSON.stringify({ Types: Types, diff --git a/src/parseTools.js b/src/parseTools.js index 08cf9b60f4922..ff9812640c83c 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -362,7 +362,7 @@ function getVectorNativeType(type) { function getSIMDName(type) { switch (type) { - case 'i32': return 'uint'; + case 'i32': return 'int'; case 'float': return 'float'; default: throw 'getSIMDName ' + type; } @@ -603,10 +603,11 @@ function parseLLVMSegment(segment) { type = segment[0].text; if (type[type.length-1] === '>' && segment[1].text[0] === '<') { // vector literal + var nativeType = getVectorNativeType(type); return { intertype: 'vector', idents: splitTokenList(segment[1].tokens).map(function(pair) { - return pair[1].text; + return parseNumerical(pair[1].text, nativeType); }), type: type }; @@ -1453,7 +1454,7 @@ function makeSetValues(ptr, pos, value, type, num, align) { // If we don't know how to handle this at compile-time, or handling it is best done in a large amount of code, call memset // TODO: optimize the case of numeric num but non-numeric value if (!isNumber(num) || !isNumber(value) || (parseInt(num)/align >= UNROLL_LOOP_MAX)) { - return '_memset(' + asmCoercion(getFastValue(ptr, '+', pos), 'i32') + ', ' + asmCoercion(value, 'i32') + ', ' + asmCoercion(num, 'i32') + ')'; + return '_memset(' + asmCoercion(getFastValue(ptr, '+', pos), 'i32') + ', ' + asmCoercion(value, 'i32') + ', ' + asmCoercion(num, 'i32') + ')|0'; } num = parseInt(num); value = parseInt(value); @@ -2371,29 +2372,28 @@ function processMathop(item) { // vector/SIMD operation Types.usesSIMD = true; switch (op) { - case 'fadd': return 'SIMD.add(' + idents[0] + ',' + idents[1] + ')'; - case 'fsub': return 'SIMD.sub(' + idents[0] + ',' + idents[1] + ')'; - case 'fmul': return 'SIMD.mul(' + idents[0] + ',' + idents[1] + ')'; - case 'fdiv': return 'SIMD.div(' + idents[0] + ',' + idents[1] + ')'; - case 'add' : return 'SIMD.addu32(' + idents[0] + ',' + idents[1] + ')'; - case 'sub' : return 'SIMD.subu32(' + idents[0] + ',' + idents[1] + ')'; - case 'mul' : return 'SIMD.mulu32(' + idents[0] + ',' + idents[1] + ')'; - case 'udiv': return 'SIMD.divu32(' + idents[0] + ',' + idents[1] + ')'; + case 'fadd': return 'SIMD.float32x4.add(' + idents[0] + ',' + idents[1] + ')'; + case 'fsub': return 'SIMD.float32x4.sub(' + idents[0] + ',' + idents[1] + ')'; + case 'fmul': return 'SIMD.float32x4.mul(' + idents[0] + ',' + idents[1] + ')'; + case 'fdiv': return 'SIMD.float32x4.div(' + idents[0] + ',' + idents[1] + ')'; + case 'add' : return 'SIMD.int32x4.add(' + idents[0] + ',' + idents[1] + ')'; + case 'sub' : return 'SIMD.int32x4.sub(' + idents[0] + ',' + idents[1] + ')'; + case 'mul' : return 'SIMD.int32x4.mul(' + idents[0] + ',' + idents[1] + ')'; case 'bitcast': { var inType = item.params[0].type; var outType = item.type; if (inType === '<4 x float>') { assert(outType === '<4 x i32>'); - return 'SIMD.float32x4BitsToUint32x4(' + idents[0] + ')'; + return 'SIMD.float32x4.bitsToInt32x4(' + idents[0] + ')'; } else { assert(inType === '<4 x i32>'); assert(outType === '<4 x float>'); - return 'SIMD.uint32x4BitsToFloat32x4(' + idents[0] + ')'; + return 'SIMD.int32x4.bitsToFloat32x4(' + idents[0] + ')'; } } - case 'and': return 'SIMD.and(' + idents[0] + ',' + idents[1] + ')'; - case 'or': return 'SIMD.or(' + idents[0] + ',' + idents[1] + ')'; - case 'xor': return 'SIMD.xor(' + idents[0] + ',' + idents[1] + ')'; + case 'and': return 'SIMD.int32x4.and(' + idents[0] + ',' + idents[1] + ')'; + case 'or': return 'SIMD.int32x4.or(' + idents[0] + ',' + idents[1] + ')'; + case 'xor': return 'SIMD.int32x4.xor(' + idents[0] + ',' + idents[1] + ')'; default: throw 'vector op todo: ' + dump(item); } } @@ -2697,7 +2697,7 @@ var simdLane = ['x', 'y', 'z', 'w']; function ensureVector(ident, base) { Types.usesSIMD = true; - return ident == 0 ? base + '32x4.zero()' : ident; + return ident == 0 ? base + '32x4.splat(0)' : ident; } function ensureValidFFIType(type) { diff --git a/src/preamble.js b/src/preamble.js index ff9200fcf4a25..832ec2c30d092 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -585,16 +585,16 @@ function UTF16ToString(ptr) { } Module['UTF16ToString'] = UTF16ToString; -// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', // null-terminated and encoded in UTF16LE form. The copy will require at most (str.length*2+1)*2 bytes of space in the HEAP. function stringToUTF16(str, outPtr) { for(var i = 0; i < str.length; ++i) { // charCodeAt returns a UTF-16 encoded code unit, so it can be directly written to the HEAP. var codeUnit = str.charCodeAt(i); // possibly a lead surrogate - {{{ makeSetValue('outPtr', 'i*2', 'codeUnit', 'i16') }}} + {{{ makeSetValue('outPtr', 'i*2', 'codeUnit', 'i16') }}}; } // Null-terminate the pointer to the HEAP. - {{{ makeSetValue('outPtr', 'str.length*2', 0, 'i16') }}} + {{{ makeSetValue('outPtr', 'str.length*2', 0, 'i16') }}}; } Module['stringToUTF16'] = stringToUTF16; @@ -620,7 +620,7 @@ function UTF32ToString(ptr) { } Module['UTF32ToString'] = UTF32ToString; -// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', // null-terminated and encoded in UTF32LE form. The copy will require at most (str.length+1)*4 bytes of space in the HEAP, // but can use less, since str.length does not return the number of characters in the string, but the number of UTF-16 code units in the string. function stringToUTF32(str, outPtr) { @@ -632,16 +632,20 @@ function stringToUTF32(str, outPtr) { var trailSurrogate = str.charCodeAt(++iCodeUnit); codeUnit = 0x10000 + ((codeUnit & 0x3FF) << 10) | (trailSurrogate & 0x3FF); } - {{{ makeSetValue('outPtr', 'iChar*4', 'codeUnit', 'i32') }}} + {{{ makeSetValue('outPtr', 'iChar*4', 'codeUnit', 'i32') }}}; ++iChar; } // Null-terminate the pointer to the HEAP. - {{{ makeSetValue('outPtr', 'iChar*4', 0, 'i32') }}} + {{{ makeSetValue('outPtr', 'iChar*4', 0, 'i32') }}}; } Module['stringToUTF32'] = stringToUTF32; function demangle(func) { try { + // Special-case the entry point, since its name differs from other name mangling. + if (func == 'Object._main' || func == '_main') { + return 'main()'; + } if (typeof func === 'number') func = Pointer_stringify(func); if (func[0] !== '_') return func; if (func[1] !== '_') return func; // C function @@ -1043,7 +1047,7 @@ function writeStringToMemory(string, buffer, dontAddNull) { var i = 0; while (i < array.length) { var chr = array[i]; - {{{ makeSetValue('buffer', 'i', 'chr', 'i8') }}} + {{{ makeSetValue('buffer', 'i', 'chr', 'i8') }}}; i = i + 1; } } @@ -1061,9 +1065,9 @@ function writeAsciiToMemory(str, buffer, dontAddNull) { #if ASSERTIONS assert(str.charCodeAt(i) === str.charCodeAt(i)&0xff); #endif - {{{ makeSetValue('buffer', 'i', 'str.charCodeAt(i)', 'i8') }}} + {{{ makeSetValue('buffer', 'i', 'str.charCodeAt(i)', 'i8') }}}; } - if (!dontAddNull) {{{ makeSetValue('buffer', 'str.length', 0, 'i8') }}} + if (!dontAddNull) {{{ makeSetValue('buffer', 'str.length', 0, 'i8') }}}; } Module['writeAsciiToMemory'] = writeAsciiToMemory; diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp index 0b4284bc30933..fc7b3ea7771cb 100644 --- a/src/relooper/Relooper.cpp +++ b/src/relooper/Relooper.cpp @@ -6,7 +6,12 @@ #include #include +#if EMSCRIPTEN #include "ministring.h" +#else +#include +typedef std::string ministring; +#endif template bool contains(const T& container, const U& contained) { return container.find(contained) != container.end(); @@ -66,11 +71,7 @@ static int AsmJS = 0; // Indenter -#if EMSCRIPTEN int Indenter::CurrIndent = 1; -#else -int Indenter::CurrIndent = 0; -#endif // Branch @@ -141,6 +142,7 @@ void Block::Render(bool InLoop) { if (!ProcessedBranchesOut.size()) return; bool SetLabel = true; // in some cases it is clear we can avoid setting label, see later + bool ForceSetLabel = Shape::IsEmulated(Parent); // A setting of the label variable (label = x) is necessary if it can // cause an impact. The main case is where we set label to x, then elsewhere @@ -209,7 +211,7 @@ void Block::Render(bool InLoop) { Target = DefaultTarget; Details = ProcessedBranchesOut[DefaultTarget]; } - bool SetCurrLabel = SetLabel && Target->IsCheckedMultipleEntry; + bool SetCurrLabel = (SetLabel && Target->IsCheckedMultipleEntry) || ForceSetLabel; bool HasFusedContent = Fused && contains(Fused->InnerMap, Target); bool HasContent = SetCurrLabel || Details->Type != Branch::Direct || HasFusedContent || Details->Code; if (iter != ProcessedBranchesOut.end()) { @@ -329,16 +331,19 @@ void LoopShape::Render(bool InLoop) { if (Next) Next->Render(InLoop); }; -/* // EmulatedShape void EmulatedShape::Render(bool InLoop) { + PrintIndented("label = %d;\n", Entry->Id); + if (Labeled) { + PrintIndented("L%d: ", Id); + } PrintIndented("while(1) {\n"); Indenter::Indent(); - PrintIndented("switch(label) {\n"); + PrintIndented("switch(label|0) {\n"); Indenter::Indent(); - for (int i = 0; i < Blocks.size(); i++) { - Block *Curr = Blocks[i]; + for (BlockSet::iterator iter = Blocks.begin(); iter != Blocks.end(); iter++) { + Block *Curr = *iter; PrintIndented("case %d: {\n", Curr->Id); Indenter::Indent(); Curr->Render(InLoop); @@ -352,11 +357,10 @@ void EmulatedShape::Render(bool InLoop) { PrintIndented("}\n"); if (Next) Next->Render(InLoop); }; -*/ // Relooper -Relooper::Relooper() : Root(NULL) { +Relooper::Relooper() : Root(NULL), Emulate(false) { } Relooper::~Relooper() { @@ -460,7 +464,7 @@ void Relooper::Calculate(Block *Entry) { } } - Pre.SplitDeadEnds(); + if (!Emulate) Pre.SplitDeadEnds(); // Recursively process the graph @@ -525,6 +529,21 @@ void Relooper::Calculate(Block *Entry) { return Simple; } + Shape *MakeEmulated(BlockSet &Blocks, Block *Entry, BlockSet &NextEntries) { + PrintDebug("creating emulated block with entry #%d and everything it can reach, %d blocks\n", Entry->Id, Blocks.size()); + EmulatedShape *Emulated = new EmulatedShape; + Notice(Emulated); + Emulated->Entry = Entry; + for (BlockSet::iterator iter = Blocks.begin(); iter != Blocks.end(); iter++) { + Block *Curr = *iter; + Emulated->Blocks.insert(Curr); + Curr->Parent = Emulated; + Solipsize(Curr, Branch::Continue, Emulated, Blocks); + } + Blocks.clear(); + return Emulated; + } + Shape *MakeLoop(BlockSet &Blocks, BlockSet& Entries, BlockSet &NextEntries) { // Find the inner blocks in this loop. Proceed backwards from the entries until // you reach a seen block, collecting as you go. @@ -836,6 +855,9 @@ void Relooper::Calculate(Block *Entry) { if (Entries->size() == 0) return Ret; if (Entries->size() == 1) { Block *Curr = *(Entries->begin()); + if (Parent->Emulate) { + Make(MakeEmulated(Blocks, Curr, *NextEntries)); + } if (Curr->BranchesIn.size() == 0) { // One entry, no looping ==> Simple Make(MakeSimple(Blocks, Curr, *NextEntries)); @@ -843,6 +865,7 @@ void Relooper::Calculate(Block *Entry) { // One entry, looping ==> Loop Make(MakeLoop(Blocks, *Entries, *NextEntries)); } + // More than one entry, try to eliminate through a Multiple groups of // independent blocks from an entry/ies. It is important to remove through // multiples as opposed to looping since the former is more performant. diff --git a/src/relooper/Relooper.h b/src/relooper/Relooper.h index f3dedf8c0ec45..e78d18e713ded 100644 --- a/src/relooper/Relooper.h +++ b/src/relooper/Relooper.h @@ -96,6 +96,7 @@ class SimpleShape; class LabeledShape; class MultipleShape; class LoopShape; +class EmulatedShape; struct Shape { int Id; // A unique identifier. Used to identify loops, labels are Lx where x is the Id. @@ -105,7 +106,8 @@ struct Shape { enum ShapeType { Simple, Multiple, - Loop + Loop, + Emulated }; ShapeType Type; @@ -118,6 +120,7 @@ struct Shape { static MultipleShape *IsMultiple(Shape *It) { return It && It->Type == Multiple ? (MultipleShape*)It : NULL; } static LoopShape *IsLoop(Shape *It) { return It && It->Type == Loop ? (LoopShape*)It : NULL; } static LabeledShape *IsLabeled(Shape *It) { return IsMultiple(It) || IsLoop(It) ? (LabeledShape*)It : NULL; } + static EmulatedShape *IsEmulated(Shape *It) { return It && It->Type == Emulated ? (EmulatedShape*)It : NULL; } // INTERNAL static int IdCounter; @@ -162,12 +165,15 @@ struct LoopShape : public LabeledShape { void Render(bool InLoop); }; -/* -struct EmulatedShape : public Shape { - std::deque Blocks; +// TODO EmulatedShape is only partially functional. Currently it can be used for the +// entire set of blocks being relooped, but not subsets. +struct EmulatedShape : public LabeledShape { + Block *Entry; + BlockSet Blocks; + + EmulatedShape() : LabeledShape(Emulated) { Labeled = true; } void Render(bool InLoop); }; -*/ // Implements the relooper algorithm for a function's blocks. // @@ -184,6 +190,7 @@ struct Relooper { std::deque Blocks; std::deque Shapes; Shape *Root; + bool Emulate; Relooper(); ~Relooper(); @@ -204,6 +211,9 @@ struct Relooper { // Sets asm.js mode on or off (default is off) static void SetAsmJSMode(int On); + + // Sets whether we must emulate everything with switch-loop code + void SetEmulate(int E) { Emulate = E; } }; typedef std::map BlockBlockSetMap; diff --git a/src/relooper/fuzzer.py b/src/relooper/fuzzer.py index 50846d1032c55..fa47583e54438 100644 --- a/src/relooper/fuzzer.py +++ b/src/relooper/fuzzer.py @@ -87,6 +87,12 @@ Relooper r; ''' + if random.random() < 0.1: + print 'emulate' + fast += ''' + r.SetEmulate(true); +''' + for i in range(num): fast += ''' r.AddBlock(b%d); ''' % i diff --git a/src/relooper/test.cpp b/src/relooper/test.cpp index fbd9c7aac6c69..773f6ee496f3b 100644 --- a/src/relooper/test.cpp +++ b/src/relooper/test.cpp @@ -258,5 +258,33 @@ int main() { puts(buffer); } + + if (1) { + Relooper::SetOutputBuffer(buffer, sizeof(buffer)); + + printf("\n\n-- If pattern, emulated --\n\n", "the_var"); + + Block *b_a = new Block("// block A\n", NULL); + Block *b_b = new Block("// block B\n", "b_check()"); + Block *b_c = new Block("// block C\n", NULL); + + b_a->AddBranchTo(b_b, "check == 10", "atob();"); + b_a->AddBranchTo(b_c, NULL, "atoc();"); + + b_b->AddBranchTo(b_c, "case 17:", "btoc();"); + b_b->AddBranchTo(b_a, NULL, NULL); + + Relooper r; + r.SetEmulate(true); + r.AddBlock(b_a); + r.AddBlock(b_b); + r.AddBlock(b_c); + + r.Calculate(b_a); + printf("\n\n", "the_var"); + r.Render(); + + puts(buffer); + } } diff --git a/src/relooper/test.txt b/src/relooper/test.txt index 2c530567eae76..9b537fd9c9cff 100644 --- a/src/relooper/test.txt +++ b/src/relooper/test.txt @@ -270,3 +270,47 @@ L46: do { } while(0); // block C + + +-- If pattern, emulated -- + + + +L50: while(1) { + switch(label) { + case 40: { + // block A + if (check == 10) { + atob(); + label = 41; + continue L50; + } else { + atoc(); + label = 42; + continue L50; + } + break; + } + case 41: { + // block B + switch (b_check()) { + case 17: { + btoc(); + label = 42; + continue L50; + break; + } + default: { + label = 40; + continue L50; + } + } + break; + } + case 42: { + // block C + break; + } + } +} + diff --git a/src/relooper/testit.sh b/src/relooper/testit.sh index 88db35fb5dd92..6e984b5a2c92c 100755 --- a/src/relooper/testit.sh +++ b/src/relooper/testit.sh @@ -2,61 +2,61 @@ echo "test" ./test &> test.out -diff -U 5 test.txt test.out +diff -w -U 5 test.txt test.out echo "test 2" ./test2 &> test2.out -diff -U 5 test2.txt test2.out +diff -w -U 5 test2.txt test2.out echo "test 3" ./test3 &> test3.out -diff -U 5 test3.txt test3.out +diff -w -U 5 test3.txt test3.out echo "test debug" ./test_debug &> test_debug.out -diff -U 5 test_debug.txt test_debug.out +diff -w -U 5 test_debug.txt test_debug.out echo "test dead" ./test_dead &> test_dead.out -diff -U 5 test_dead.txt test_dead.out +diff -w -U 5 test_dead.txt test_dead.out echo "test 4" ./test4 &> test4.out -diff -U 5 test4.txt test4.out +diff -w -U 5 test4.txt test4.out echo "test 5" ./test5 &> test5.out -diff -U 5 test5.txt test5.out +diff -w -U 5 test5.txt test5.out echo "test 6" ./test6 &> test6.out -diff -U 5 test6.txt test6.out +diff -w -U 5 test6.txt test6.out echo "test inf" ./test_inf &> test_inf.out -diff -U 5 test_inf.txt test_inf.out +diff -w -U 5 test_inf.txt test_inf.out echo "test fuzz1" ./test_fuzz1 &> test_fuzz1.out -diff -U 5 test_fuzz1.txt test_fuzz1.out +diff -w -U 5 test_fuzz1.txt test_fuzz1.out echo "test fuzz2" ./test_fuzz2 &> test_fuzz2.out -diff -U 5 test_fuzz2.txt test_fuzz2.out +diff -w -U 5 test_fuzz2.txt test_fuzz2.out echo "test fuzz3" ./test_fuzz3 &> test_fuzz3.out -diff -U 5 test_fuzz3.txt test_fuzz3.out +diff -w -U 5 test_fuzz3.txt test_fuzz3.out echo "test fuzz4" ./test_fuzz4 &> test_fuzz4.out -diff -U 5 test_fuzz4.txt test_fuzz4.out +diff -w -U 5 test_fuzz4.txt test_fuzz4.out echo "test fuzz5" ./test_fuzz5 &> test_fuzz5.out -diff -U 5 test_fuzz5.txt test_fuzz5.out +diff -w -U 5 test_fuzz5.txt test_fuzz5.out echo "test fuzz6" ./test_fuzz6 &> test_fuzz6.out -diff -U 5 test_fuzz6.txt test_fuzz6.out +diff -w -U 5 test_fuzz6.txt test_fuzz6.out diff --git a/src/runtime.js b/src/runtime.js index dedaf5eae6033..8ba5d08d5c924 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -185,8 +185,11 @@ var Runtime = { // type can be a native type or a struct (or null, for structs we only look at size here) getAlignSize: function(type, size, vararg) { // we align i64s and doubles on 64-bit boundaries, unlike x86 +#if TARGET_LE32 == 1 + if (vararg) return 8; +#endif #if TARGET_LE32 - if (type == 'i64' || type == 'double' || vararg) return 8; + if (!vararg && (type == 'i64' || type == 'double')) return 8; if (!type) return Math.min(size, 8); // align structures internally to 64 bits #endif return Math.min(size || (type ? Runtime.getNativeFieldSize(type) : 0), Runtime.QUANTUM_SIZE); diff --git a/src/settings.js b/src/settings.js index bc665973367c1..753e2367763df 100644 --- a/src/settings.js +++ b/src/settings.js @@ -23,7 +23,8 @@ var QUANTUM_SIZE = 4; // This is the size of an individual field in a structure. // Changing this from the default of 4 is deprecated. var TARGET_X86 = 0; // For i386-pc-linux-gnu -var TARGET_LE32 = 1; // For le32-unknown-nacl +var TARGET_LE32 = 1; // For le32-unknown-nacl. 1 is normal, 2 is for the fastcomp llvm + // backend using pnacl abi simplification var CORRECT_SIGNS = 1; // Whether we make sure to convert unsigned values to signed values. // Decreases performance with additional runtime checks. Might not be @@ -223,6 +224,10 @@ var GL_UNSAFE_OPTS = 1; // Enables some potentially-unsafe optimizations in GL e var FULL_ES2 = 0; // Forces support for all GLES2 features, not just the WebGL-friendly subset. var LEGACY_GL_EMULATION = 0; // Includes code to emulate various desktop GL features. Incomplete but useful // in some cases, see https://github.com/kripken/emscripten/wiki/OpenGL-support +var GL_FFP_ONLY = 0; // If you specified LEGACY_GL_EMULATION = 1 and only use fixed function pipeline in your code, + // you can also set this to 1 to signal the GL emulation layer that it can perform extra + // optimizations by knowing that the user code does not use shaders at all. If + // LEGACY_GL_EMULATION = 0, this setting has no effect. var STB_IMAGE = 0; // Enables building of stb-image, a tiny public-domain library for decoding images, allowing // decoding of images without using the browser's built-in decoders. The benefit is that this diff --git a/src/shell.html b/src/shell.html index 53a4fffba0339..efb9e91db9a9f 100644 --- a/src/shell.html +++ b/src/shell.html @@ -50,7 +50,7 @@ //text = text.replace(/>/g, ">"); //text = text.replace('\n', '
', 'g'); element.value += text + "\n"; - element.scrollTop = 99999; // focus on bottom + element.scrollTop = element.scrollHeight; // focus on bottom }; })(), printErr: function(text) { diff --git a/src/simd.js b/src/simd.js index bbb12d0a4c0f6..6e3e3675a5bf6 100644 --- a/src/simd.js +++ b/src/simd.js @@ -20,8 +20,10 @@ https://github.com/johnmccutchan/ecmascript_simd/blob/master/src/ecmascript_simd.js */ +"use strict"; + /** - * Construct a new instance of a float32x4 number. + * Construct a new instance of float32x4 number. * @param {double} value used for x lane. * @param {double} value used for y lane. * @param {double} value used for z lane. @@ -40,7 +42,7 @@ function float32x4(x, y, z, w) { } /** - * Construct a new instance of a float32x4 number with 0.0 in all lanes. + * Construct a new instance of float32x4 number with 0.0 in all lanes. * @constructor */ float32x4.zero = function() { @@ -48,7 +50,7 @@ float32x4.zero = function() { } /** - * Construct a new instance of a float32x4 number with the same value + * Construct a new instance of float32x4 number with the same value * in all lanes. * @param {double} value used for all lanes. * @constructor @@ -87,18 +89,18 @@ Object.defineProperty(float32x4.prototype, 'signMask', { }); /** - * Construct a new instance of a uint32x4 number. + * Construct a new instance of int32x4 number. * @param {integer} 32-bit unsigned value used for x lane. * @param {integer} 32-bit unsigned value used for y lane. * @param {integer} 32-bit unsigned value used for z lane. * @param {integer} 32-bit unsigned value used for w lane. * @constructor */ -function uint32x4(x, y, z, w) { - if (!(this instanceof uint32x4)) { - return new uint32x4(x, y, z, w); +function int32x4(x, y, z, w) { + if (!(this instanceof int32x4)) { + return new int32x4(x, y, z, w); } - this.storage_ = new Uint32Array(4); + this.storage_ = new Int32Array(4); this.storage_[0] = x; this.storage_[1] = y; this.storage_[2] = z; @@ -106,7 +108,7 @@ function uint32x4(x, y, z, w) { } /** - * Construct a new instance of a uint32x4 number with 0xFFFFFFFF or 0x0 in each + * Construct a new instance of int32x4 number with 0xFFFFFFFF or 0x0 in each * lane, depending on the truth value in x, y, z, and w. * @param {boolean} flag used for x lane. * @param {boolean} flag used for y lane. @@ -114,59 +116,59 @@ function uint32x4(x, y, z, w) { * @param {boolean} flag used for w lane. * @constructor */ -uint32x4.bool = function(x, y, z, w) { - return uint32x4(x ? 0xFFFFFFFF : 0x0, - y ? 0xFFFFFFFF : 0x0, - z ? 0xFFFFFFFF : 0x0, - w ? 0xFFFFFFFF : 0x0); +int32x4.bool = function(x, y, z, w) { + return int32x4(x ? -1 : 0x0, + y ? -1 : 0x0, + z ? -1 : 0x0, + w ? -1 : 0x0); } /** - * Construct a new instance of a uint32x4 number with the same value + * Construct a new instance of int32x4 number with the same value * in all lanes. * @param {integer} value used for all lanes. * @constructor */ -uint32x4.splat = function(s) { - return uint32x4(s, s, s, s); +int32x4.splat = function(s) { + return int32x4(s, s, s, s); } -Object.defineProperty(uint32x4.prototype, 'x', { +Object.defineProperty(int32x4.prototype, 'x', { get: function() { return this.storage_[0]; } }); -Object.defineProperty(uint32x4.prototype, 'y', { +Object.defineProperty(int32x4.prototype, 'y', { get: function() { return this.storage_[1]; } }); -Object.defineProperty(uint32x4.prototype, 'z', { +Object.defineProperty(int32x4.prototype, 'z', { get: function() { return this.storage_[2]; } }); -Object.defineProperty(uint32x4.prototype, 'w', +Object.defineProperty(int32x4.prototype, 'w', { get: function() { return this.storage_[3]; } }); -Object.defineProperty(uint32x4.prototype, 'flagX', { +Object.defineProperty(int32x4.prototype, 'flagX', { get: function() { return this.storage_[0] != 0x0; } }); -Object.defineProperty(uint32x4.prototype, 'flagY', { +Object.defineProperty(int32x4.prototype, 'flagY', { get: function() { return this.storage_[1] != 0x0; } }); -Object.defineProperty(uint32x4.prototype, 'flagZ', { +Object.defineProperty(int32x4.prototype, 'flagZ', { get: function() { return this.storage_[2] != 0x0; } }); -Object.defineProperty(uint32x4.prototype, 'flagW', +Object.defineProperty(int32x4.prototype, 'flagW', { get: function() { return this.storage_[3] != 0x0; } }); /** * Extract the sign bit from each lane return them in the first 4 bits. */ -Object.defineProperty(uint32x4.prototype, 'signMask', { +Object.defineProperty(int32x4.prototype, 'signMask', { get: function() { var mx = (this.storage_[0] & 0x80000000) >>> 31; var my = (this.storage_[1] & 0x80000000) >>> 31; @@ -287,414 +289,583 @@ Float32x4Array.prototype.setAt = function(i, v) { this.storage_[i*4+3] = v.w; } + +function Int32x4Array(a, b, c) { + + function isNumber(o) { + return typeof o == "number" || (typeof o == "object" && o.constructor === Number); + } + + function isTypedArray(o) { + return (o instanceof Int8Array) || + (o instanceof Uint8Array) || + (o instanceof Uint8ClampedArray) || + (o instanceof Int16Array) || + (o instanceof Uint16Array) || + (o instanceof Int32Array) || + (o instanceof Uint32Array) || + (o instanceof Float32Array) || + (o instanceof Float64Array) || + (o instanceof Int32x4Array) || + (o instanceof Float32x4Array); + } + + function isArrayBuffer(o) { + return (o instanceof ArrayBuffer); + } + + if (isNumber(a)) { + this.storage_ = new Int32Array(a*4); + this.length_ = a; + this.byteOffset_ = 0; + return; + } else if (isTypedArray(a)) { + if (!(a instanceof Int32x4Array)) { + throw "Copying typed array of non-Int32x4Array is unimplemented."; + } + this.storage_ = new Int32Array(a.length * 4); + this.length_ = a.length; + this.byteOffset_ = 0; + // Copy floats. + for (var i = 0; i < a.length*4; i++) { + this.storage_[i] = a.storage_[i]; + } + } else if (isArrayBuffer(a)) { + if ((b != undefined) && (b % Int32x4Array.BYTES_PER_ELEMENT) != 0) { + throw "byteOffset must be a multiple of 16."; + } + if (c != undefined) { + c *= 4; + this.storage_ = new Int32Array(a, b, c); + } + else { + // Note: new Int32Array(a, b) is NOT equivalent to new Float32Array(a, b, undefined) + this.storage_ = new Int32Array(a, b); + } + this.length_ = this.storage_.length / 4; + this.byteOffset_ = b != undefined ? b : 0; + } else { + throw "Unknown type of first argument."; + } +} + +Object.defineProperty(Int32x4Array.prototype, 'length', + { get: function() { return this.length_; } +}); + +Object.defineProperty(Int32x4Array.prototype, 'byteLength', + { get: function() { return this.length_ * Int32x4Array.BYTES_PER_ELEMENT; } +}); + +Object.defineProperty(Int32x4Array, 'BYTES_PER_ELEMENT', + { get: function() { return 16; } +}); + +Object.defineProperty(Int32x4Array.prototype, 'BYTES_PER_ELEMENT', + { get: function() { return 16; } +}); + +Object.defineProperty(Int32x4Array.prototype, 'byteOffset', + { get: function() { return this.byteOffset_; } +}); + +Object.defineProperty(Int32x4Array.prototype, 'buffer', + { get: function() { return this.storage_.buffer; } +}); + +Int32x4Array.prototype.getAt = function(i) { + if (i < 0) { + throw "Index must be >= 0."; + } + if (i >= this.length) { + throw "Index out of bounds."; + } + var x = this.storage_[i*4+0]; + var y = this.storage_[i*4+1]; + var z = this.storage_[i*4+2]; + var w = this.storage_[i*4+3]; + return float32x4(x, y, z, w); +} + +Int32x4Array.prototype.setAt = function(i, v) { + if (i < 0) { + throw "Index must be >= 0."; + } + if (i >= this.length) { + throw "Index out of bounds."; + } + if (!(v instanceof int32x4)) { + throw "Value is not a int32x4."; + } + this.storage_[i*4+0] = v.x; + this.storage_[i*4+1] = v.y; + this.storage_[i*4+2] = v.z; + this.storage_[i*4+3] = v.w; +} + var SIMD = (function () { return { - /** - * @return {float32x4} New instance of float32x4 with absolute values of - * t. - */ - abs: function(t) { - return new float32x4(Math.abs(t.x), Math.abs(t.y), Math.abs(t.z), - Math.abs(t.w)); - }, - /** - * @return {float32x4} New instance of float32x4 with negated values of - * t. - */ - neg: function(t) { - return new float32x4(-t.x, -t.y, -t.z, -t.w); - }, - /** - * @return {float32x4} New instance of float32x4 with a + b. - */ - add: function(a, b) { - return new float32x4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); - }, - /** - * @return {float32x4} New instance of float32x4 with a - b. - */ - sub: function(a, b) { - return new float32x4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); - }, - /** - * @return {float32x4} New instance of float32x4 with a * b. - */ - mul: function(a, b) { - return new float32x4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); - }, - /** - * @return {float32x4} New instance of float32x4 with a / b. - */ - div: function(a, b) { - return new float32x4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); - }, - /** - * @return {float32x4} New instance of float32x4 with t's values clamped - * between lowerLimit and upperLimit. - */ - clamp: function(t, lowerLimit, upperLimit) { - var cx = t.x < lowerLimit.x ? lowerLimit.x : t.x; - var cy = t.y < lowerLimit.y ? lowerLimit.y : t.y; - var cz = t.z < lowerLimit.z ? lowerLimit.z : t.z; - var cw = t.w < lowerLimit.w ? lowerLimit.w : t.w; - cx = cx > upperLimit.x ? upperLimit.x : cx; - cy = cy > upperLimit.y ? upperLimit.y : cy; - cz = cz > upperLimit.z ? upperLimit.z : cz; - cw = cw > upperLimit.w ? upperLimit.w : cw; - return new float32x4(cx, cy, cz, cw); - }, - /** - * @return {float32x4} New instance of float32x4 with the minimum value of - * t and other. - */ - min: function(t, other) { - var cx = t.x > other.x ? other.x : t.x; - var cy = t.y > other.y ? other.y : t.y; - var cz = t.z > other.z ? other.z : t.z; - var cw = t.w > other.w ? other.w : t.w; - return new float32x4(cx, cy, cz, cw); - }, - /** - * @return {float32x4} New instance of float32x4 with the maximum value of - * t and other. - */ - max: function(t, other) { - var cx = t.x < other.x ? other.x : t.x; - var cy = t.y < other.y ? other.y : t.y; - var cz = t.z < other.z ? other.z : t.z; - var cw = t.w < other.w ? other.w : t.w; - return new float32x4(cx, cy, cz, cw); - }, - /** - * @return {float32x4} New instance of float32x4 with reciprocal value of - * t. - */ - reciprocal: function(t) { - return new float32x4(1.0 / t.x, 1.0 / t.y, 1.0 / t.z, 1.0 / t.w); - }, - /** - * @return {float32x4} New instance of float32x4 with square root of the - * reciprocal value of t. - */ - reciprocalSqrt: function(t) { - return new float32x4(Math.sqrt(1.0 / t.x), Math.sqrt(1.0 / t.y), - Math.sqrt(1.0 / t.z), Math.sqrt(1.0 / t.w)); - }, - /** - * @return {float32x4} New instance of float32x4 with values of t - * scaled by s. - */ - scale: function(t, s) { - return new float32x4(s * t.x, s * t.y, s * t.z, s * t.w); - }, - /** - * @return {float32x4} New instance of float32x4 with square root of - * values of t. - */ - sqrt: function(t) { - return new float32x4(Math.sqrt(t.x), Math.sqrt(t.y), - Math.sqrt(t.z), Math.sqrt(t.w)); - }, - /** - * @param {float32x4} t An instance of float32x4 to be shuffled. - * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX. - * @return {float32x4} New instance of float32x4 with lanes shuffled. - */ - shuffle: function(t, mask) { - var _x = (mask) & 0x3; - var _y = (mask >> 2) & 0x3; - var _z = (mask >> 4) & 0x3; - var _w = (mask >> 6) & 0x3; - return new float32x4(t.storage_[_x], t.storage_[_y], t.storage_[_z], - t.storage_[_w]); + float32x4: { + /** + * @return {float32x4} New instance of float32x4 with absolute values of + * t. + */ + abs: function(t) { + return new float32x4(Math.abs(t.x), Math.abs(t.y), Math.abs(t.z), + Math.abs(t.w)); + }, + /** + * @return {float32x4} New instance of float32x4 with negated values of + * t. + */ + neg: function(t) { + return new float32x4(-t.x, -t.y, -t.z, -t.w); + }, + /** + * @return {float32x4} New instance of float32x4 with a + b. + */ + add: function(a, b) { + return new float32x4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); + }, + /** + * @return {float32x4} New instance of float32x4 with a - b. + */ + sub: function(a, b) { + return new float32x4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); + }, + /** + * @return {float32x4} New instance of float32x4 with a * b. + */ + mul: function(a, b) { + return new float32x4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); + }, + /** + * @return {float32x4} New instance of float32x4 with a / b. + */ + div: function(a, b) { + return new float32x4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); + }, + /** + * @return {float32x4} New instance of float32x4 with t's values clamped + * between lowerLimit and upperLimit. + */ + clamp: function(t, lowerLimit, upperLimit) { + var cx = t.x < lowerLimit.x ? lowerLimit.x : t.x; + var cy = t.y < lowerLimit.y ? lowerLimit.y : t.y; + var cz = t.z < lowerLimit.z ? lowerLimit.z : t.z; + var cw = t.w < lowerLimit.w ? lowerLimit.w : t.w; + cx = cx > upperLimit.x ? upperLimit.x : cx; + cy = cy > upperLimit.y ? upperLimit.y : cy; + cz = cz > upperLimit.z ? upperLimit.z : cz; + cw = cw > upperLimit.w ? upperLimit.w : cw; + return new float32x4(cx, cy, cz, cw); + }, + /** + * @return {float32x4} New instance of float32x4 with the minimum value of + * t and other. + */ + min: function(t, other) { + var cx = t.x > other.x ? other.x : t.x; + var cy = t.y > other.y ? other.y : t.y; + var cz = t.z > other.z ? other.z : t.z; + var cw = t.w > other.w ? other.w : t.w; + return new float32x4(cx, cy, cz, cw); + }, + /** + * @return {float32x4} New instance of float32x4 with the maximum value of + * t and other. + */ + max: function(t, other) { + var cx = t.x < other.x ? other.x : t.x; + var cy = t.y < other.y ? other.y : t.y; + var cz = t.z < other.z ? other.z : t.z; + var cw = t.w < other.w ? other.w : t.w; + return new float32x4(cx, cy, cz, cw); + }, + /** + * @return {float32x4} New instance of float32x4 with reciprocal value of + * t. + */ + reciprocal: function(t) { + return new float32x4(1.0 / t.x, 1.0 / t.y, 1.0 / t.z, 1.0 / t.w); + }, + /** + * @return {float32x4} New instance of float32x4 with square root of the + * reciprocal value of t. + */ + reciprocalSqrt: function(t) { + return new float32x4(Math.sqrt(1.0 / t.x), Math.sqrt(1.0 / t.y), + Math.sqrt(1.0 / t.z), Math.sqrt(1.0 / t.w)); + }, + /** + * @return {float32x4} New instance of float32x4 with values of t + * scaled by s. + */ + scale: function(t, s) { + return new float32x4(s * t.x, s * t.y, s * t.z, s * t.w); + }, + /** + * @return {float32x4} New instance of float32x4 with square root of + * values of t. + */ + sqrt: function(t) { + return new float32x4(Math.sqrt(t.x), Math.sqrt(t.y), + Math.sqrt(t.z), Math.sqrt(t.w)); + }, + /** + * @param {float32x4} t An instance of float32x4 to be shuffled. + * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX. + * @return {float32x4} New instance of float32x4 with lanes shuffled. + */ + shuffle: function(t, mask) { + var _x = (mask) & 0x3; + var _y = (mask >> 2) & 0x3; + var _z = (mask >> 4) & 0x3; + var _w = (mask >> 6) & 0x3; + return new float32x4(t.storage_[_x], t.storage_[_y], t.storage_[_z], + t.storage_[_w]); + }, + /** + * @param {float32x4} t1 An instance of float32x4 to be shuffled. XY lanes in result + * @param {float32x4} t2 An instance of float32x4 to be shuffled. ZW lanes in result + * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX. + * @return {float32x4} New instance of float32x4 with lanes shuffled. + */ + shuffleMix: function(t1, t2, mask) { + var _x = (mask) & 0x3; + var _y = (mask >> 2) & 0x3; + var _z = (mask >> 4) & 0x3; + var _w = (mask >> 6) & 0x3; + return new float32x4(t1.storage_[_x], t1.storage_[_y], t2.storage_[_z], + t2.storage_[_w]); + }, + /** + * @param {double} value used for x lane. + * @return {float32x4} New instance of float32x4 with the values in t and + * x replaced with {x}. + */ + withX: function(t, x) { + return new float32x4(x, t.y, t.z, t.w); + }, + /** + * @param {double} value used for y lane. + * @return {float32x4} New instance of float32x4 with the values in t and + * y replaced with {y}. + */ + withY: function(t, y) { + return new float32x4(t.x, y, t.z, t.w); + }, + /** + * @param {double} value used for z lane. + * @return {float32x4} New instance of float32x4 with the values in t and + * z replaced with {z}. + */ + withZ: function(t, z) { + return new float32x4(t.x, t.y, z, t.w); + }, + /** + * @param {double} value used for w lane. + * @return {float32x4} New instance of float32x4 with the values in t and + * w replaced with {w}. + */ + withW: function(t, w) { + return new float32x4(t.x, t.y, t.z, w); + }, + /** + * @param {float32x4} t An instance of float32x4. + * @param {float32x4} other An instance of float32x4. + * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on + * the result of t < other. + */ + lessThan: function(t, other) { + var cx = t.x < other.x; + var cy = t.y < other.y; + var cz = t.z < other.z; + var cw = t.w < other.w; + return int32x4.bool(cx, cy, cz, cw); + }, + /** + * @param {float32x4} t An instance of float32x4. + * @param {float32x4} other An instance of float32x4. + * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on + * the result of t <= other. + */ + lessThanOrEqual: function(t, other) { + var cx = t.x <= other.x; + var cy = t.y <= other.y; + var cz = t.z <= other.z; + var cw = t.w <= other.w; + return int32x4.bool(cx, cy, cz, cw); + }, + /** + * @param {float32x4} t An instance of float32x4. + * @param {float32x4} other An instance of float32x4. + * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on + * the result of t == other. + */ + equal: function(t, other) { + var cx = t.x == other.x; + var cy = t.y == other.y; + var cz = t.z == other.z; + var cw = t.w == other.w; + return int32x4.bool(cx, cy, cz, cw); + }, + /** + * @param {float32x4} t An instance of float32x4. + * @param {float32x4} other An instance of float32x4. + * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on + * the result of t != other. + */ + notEqual: function(t, other) { + var cx = t.x != other.x; + var cy = t.y != other.y; + var cz = t.z != other.z; + var cw = t.w != other.w; + return int32x4.bool(cx, cy, cz, cw); + }, + /** + * @param {float32x4} t An instance of float32x4. + * @param {float32x4} other An instance of float32x4. + * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on + * the result of t >= other. + */ + greaterThanOrEqual: function(t, other) { + var cx = t.x >= other.x; + var cy = t.y >= other.y; + var cz = t.z >= other.z; + var cw = t.w >= other.w; + return int32x4.bool(cx, cy, cz, cw); + }, + /** + * @param {float32x4} t An instance of float32x4. + * @param {float32x4} other An instance of float32x4. + * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on + * the result of t > other. + */ + greaterThan: function(t, other) { + var cx = t.x > other.x; + var cy = t.y > other.y; + var cz = t.z > other.z; + var cw = t.w > other.w; + return int32x4.bool(cx, cy, cz, cw); + }, + /** + * @param {float32x4} t An instance of float32x4. + * @return {int32x4} a bit-wise copy of t as a int32x4. + */ + bitsToInt32x4: function(t) { + var alias = new Int32Array(t.storage_.buffer); + return new int32x4(alias[0], alias[1], alias[2], alias[3]); + }, + /** + * @param {float32x4} t An instance of float32x4. + * @return {int32x4} with a integer to float conversion of t. + */ + toInt32x4: function(t) { + var a = new int32x4(t.storage_[0], t.storage_[1], t.storage_[2], + t.storage_[3]); + return a; + } }, - /** - * @param {double} value used for x lane. - * @return {float32x4} New instance of float32x4 with the values in t and - * x replaced with {x}. - */ - withX: function(t, x) { - return new float32x4(x, t.y, t.z, t.w); - }, - /** - * @param {double} value used for y lane. - * @return {float32x4} New instance of float32x4 with the values in t and - * y replaced with {y}. - */ - withY: function(t, y) { - return new float32x4(t.x, y, t.z, t.w); - }, - /** - * @param {double} value used for z lane. - * @return {float32x4} New instance of float32x4 with the values in t and - * z replaced with {z}. - */ - withZ: function(t, z) { - return new float32x4(t.x, t.y, z, t.w); - }, - /** - * @param {double} value used for w lane. - * @return {float32x4} New instance of float32x4 with the values in t and - * w replaced with {w}. - */ - withW: function(t, w) { - return new float32x4(t.x, t.y, t.z, w); - }, - /** - * @param {float32x4} t An instance of a float32x4. - * @param {float32x4} other An instance of a float32x4. - * @return {uint32x4} 0xFFFFFFFF or 0x0 in each lane depending on - * the result of t < other. - */ - lessThan: function(t, other) { - var cx = t.x < other.x; - var cy = t.y < other.y; - var cz = t.z < other.z; - var cw = t.w < other.w; - return uint32x4.bool(cx, cy, cz, cw); - }, - /** - * @param {float32x4} t An instance of a float32x4. - * @param {float32x4} other An instance of a float32x4. - * @return {uint32x4} 0xFFFFFFFF or 0x0 in each lane depending on - * the result of t <= other. - */ - lessThanOrEqual: function(t, other) { - var cx = t.x <= other.x; - var cy = t.y <= other.y; - var cz = t.z <= other.z; - var cw = t.w <= other.w; - return uint32x4.bool(cx, cy, cz, cw); - }, - /** - * @param {float32x4} t An instance of a float32x4. - * @param {float32x4} other An instance of a float32x4. - * @return {uint32x4} 0xFFFFFFFF or 0x0 in each lane depending on - * the result of t == other. - */ - equal: function(t, other) { - var cx = t.x == other.x; - var cy = t.y == other.y; - var cz = t.z == other.z; - var cw = t.w == other.w; - return uint32x4.bool(cx, cy, cz, cw); - }, - /** - * @param {float32x4} t An instance of a float32x4. - * @param {float32x4} other An instance of a float32x4. - * @return {uint32x4} 0xFFFFFFFF or 0x0 in each lane depending on - * the result of t != other. - */ - notEqual: function(t, other) { - var cx = t.x != other.x; - var cy = t.y != other.y; - var cz = t.z != other.z; - var cw = t.w != other.w; - return uint32x4.bool(cx, cy, cz, cw); - }, - /** - * @param {float32x4} t An instance of a float32x4. - * @param {float32x4} other An instance of a float32x4. - * @return {uint32x4} 0xFFFFFFFF or 0x0 in each lane depending on - * the result of t >= other. - */ - greaterThanOrEqual: function(t, other) { - var cx = t.x >= other.x; - var cy = t.y >= other.y; - var cz = t.z >= other.z; - var cw = t.w >= other.w; - return uint32x4.bool(cx, cy, cz, cw); - }, - /** - * @param {float32x4} t An instance of a float32x4. - * @param {float32x4} other An instance of a float32x4. - * @return {uint32x4} 0xFFFFFFFF or 0x0 in each lane depending on - * the result of t > other. - */ - greaterThan: function(t, other) { - var cx = t.x > other.x; - var cy = t.y > other.y; - var cz = t.z > other.z; - var cw = t.w > other.w; - return uint32x4.bool(cx, cy, cz, cw); - }, - /** - * @param {uint32x4} a An instance of a uint32x4. - * @param {uint32x4} b An instance of a uint32x4. - * @return {uint32x4} New instance of uint32x4 with values of a & b. - */ - and: function(a, b) { - return new uint32x4(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w); - }, - /** - * @param {uint32x4} a An instance of a uint32x4. - * @param {uint32x4} b An instance of a uint32x4. - * @return {uint32x4} New instance of uint32x4 with values of a | b. - */ - or: function(a, b) { - return new uint32x4(a.x | b.x, a.y | b.y, a.z | b.z, a.w | b.w); - }, - /** - * @param {uint32x4} a An instance of a uint32x4. - * @param {uint32x4} b An instance of a uint32x4. - * @return {uint32x4} New instance of uint32x4 with values of a ^ b. - */ - xor: function(a, b) { - return new uint32x4(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z, a.w ^ b.w); - }, - /** - * @param {uint32x4} t An instance of a uint32x4. - * @return {uint32x4} New instance of uint32x4 with values of ~a - */ - negu32: function(t) { - return new uint32x4(~t.x, ~t.y, ~t.z, ~t.w); - }, - /** - * @param {uint32x4} a An instance of uint32x4. - * @param {uint32x4} b An instance of uint32x4. - * @return {uint32x4} New instance of uint32x4 with values of a + b. - */ - addu32: function(a, b) { - return new uint32x4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); - }, - /** - * @param {uint32x4} a An instance of uint32x4. - * @param {uint32x4} b An instance of uint32x4. - * @return {uint32x4} New instance of uint32x4 with values of a - b. - */ - subu32: function(a, b) { - return new uint32x4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); - }, - /** - * @param {uint32x4} a An instance of uint32x4. - * @param {uint32x4} b An instance of uint32x4. - * @return {uint32x4} New instance of uint32x4 with values of a * b. - */ - mulu32: function(a, b) { - return new uint32x4(Math.imul(a.x, b.x), Math.imul(a.y, b.y), - Math.imul(a.z, b.z), Math.imul(a.w, b.w)); - }, - /** - * @param {float32x4} - */ - select: function(t, trueValue, falseValue) { - var tv = SIMD.float32x4BitsToUint32x4(trueValue); - var fv = SIMD.float32x4BitsToUint32x4(falseValue); - var tr = SIMD.and(t, tv); - var fr = SIMD.and(SIMD.negu32(t), fv); - return SIMD.uint32x4BitsToFloat32x4(SIMD.or(tr, fr)); - }, - /** - * @param {uint32x4} t An instance of a uint32x4. - * @param {integer} 32-bit value used for x lane. - * @return {uint32x4} New instance of uint32x4 with the values in t and - * x lane replaced with {x}. - */ - withXu32: function(t, x) { - return new uint32x4(x, t.y, t.z, t.w); - }, - /** - * param {uint32x4} t An instance of a uint32x4. - * @param {integer} 32-bit value used for y lane. - * @return {uint32x4} New instance of uint32x4 with the values in t and - * y lane replaced with {y}. - */ - withYu32: function(t, y) { - return new uint32x4(t.x, y, t.z, t.w); - }, - /** - * @param {uint32x4} t An instance of a uint32x4. - * @param {integer} 32-bit value used for z lane. - * @return {uint32x4} New instance of uint32x4 with the values in t and - * z lane replaced with {z}. - */ - withZu32: function(t, z) { - return new uint32x4(t.x, t.y, z, t.w); - }, - /** - * @param {integer} 32-bit value used for w lane. - * @return {uint32x4} New instance of uint32x4 with the values in t and - * w lane replaced with {w}. - */ - withWu32: function(t, w) { - return new uint32x4(t.x, t.y, t.z, w); - }, - /** - * @param {uint32x4} t An instance of a uint32x4. - * @param {boolean} x flag used for x lane. - * @return {uint32x4} New instance of uint32x4 with the values in t and - * x lane replaced with {x}. - */ - withFlagX: function(t, flagX) { - var x = flagX ? 0xFFFFFFFF : 0x0; - return new uint32x4(x, t.y, t.z, t.w); - }, - /** - * @param {uint32x4} t An instance of a uint32x4. - * @param {boolean} y flag used for y lane. - * @return {uint32x4} New instance of uint32x4 with the values in t and - * y lane replaced with {y}. - */ - withFlagY: function(t, flagY) { - var y = flagY ? 0xFFFFFFFF : 0x0; - return new uint32x4(t.x, y, t.z, t.w); - }, - /** - * @param {uint32x4} t An instance of a uint32x4. - * @param {boolean} z flag used for z lane. - * @return {uint32x4} New instance of uint32x4 with the values in t and - * z lane replaced with {z}. - */ - withFlagZ: function(t, flagZ) { - var z = flagZ ? 0xFFFFFFFF : 0x0; - return new uint32x4(t.x, t.y, z, t.w); - }, - /** - * @param {uint32x4} t An instance of a uint32x4. - * @param {boolean} w flag used for w lane. - * @return {uint32x4} New instance of uint32x4 with the values in t and - * w lane replaced with {w}. - */ - withFlagW: function(t, flagW) { - var w = flagW ? 0xFFFFFFFF : 0x0; - return new uint32x4(t.x, t.y, t.z, w); - }, - /** - * @param {float32x4} t An instance of a float32x4. - * @return {uint32x4} a bit-wise copy of t as a uint32x4. - */ - float32x4BitsToUint32x4: function(t) { - var alias = new Uint32Array(t.storage_.buffer); - return new uint32x4(alias[0], alias[1], alias[2], alias[3]); - }, - /** - * @param {uint32x4} t An instance of a uint32x4. - * @return {float32x4} a bit-wise copy of t as a float32x4. - */ - uint32x4BitsToFloat32x4: function(t) { - var alias = new Float32Array(t.storage_.buffer); - return new float32x4(alias[0], alias[1], alias[2], alias[3]); - }, - /** - * @param {uint32x4} t An instance of a uint32x4. - * @return {float32x4} with a float to integer conversion copy of t. - */ - uint32x4ToFloat32x4: function(t) { - var a = float32x4.zero(); - a.storage_[0] = t.storage_[0]; - a.storage_[1] = t.storage_[1]; - a.storage_[2] = t.storage_[2]; - a.storage_[3] = t.storage_[3]; - return a; - }, - /** - * @param {float32x4} t An instance of a float32x4. - * @return {uint32x4} with a integer to float conversion of t. - */ - float32x4ToUint32x4: function(t) { - var a = new uint32x4(t.storage_[0], t.storage_[1], t.storage_[2], - t.storage_[3]); - return a; + int32x4: { + /** + * @param {int32x4} a An instance of int32x4. + * @param {int32x4} b An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of a & b. + */ + and: function(a, b) { + return new int32x4(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w); + }, + /** + * @param {int32x4} a An instance of int32x4. + * @param {int32x4} b An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of a | b. + */ + or: function(a, b) { + return new int32x4(a.x | b.x, a.y | b.y, a.z | b.z, a.w | b.w); + }, + /** + * @param {int32x4} a An instance of int32x4. + * @param {int32x4} b An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of a ^ b. + */ + xor: function(a, b) { + return new int32x4(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z, a.w ^ b.w); + }, + /** + * @param {int32x4} t An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of ~t + */ + not: function(t) { + return new int32x4(~t.x, ~t.y, ~t.z, ~t.w); + }, + /** + * @param {int32x4} t An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of -t + */ + neg: function(t) { + return new int32x4(-t.x, -t.y, -t.z, -t.w); + }, + /** + * @param {int32x4} a An instance of int32x4. + * @param {int32x4} b An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of a + b. + */ + add: function(a, b) { + return new int32x4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); + }, + /** + * @param {int32x4} a An instance of int32x4. + * @param {int32x4} b An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of a - b. + */ + sub: function(a, b) { + return new int32x4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); + }, + /** + * @param {int32x4} a An instance of int32x4. + * @param {int32x4} b An instance of int32x4. + * @return {int32x4} New instance of int32x4 with values of a * b. + */ + mul: function(a, b) { + return new int32x4(Math.imul(a.x, b.x), Math.imul(a.y, b.y), + Math.imul(a.z, b.z), Math.imul(a.w, b.w)); + }, + /** + * @param {int32x4} t An instance of float32x4 to be shuffled. + * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX. + * @return {int32x4} New instance of float32x4 with lanes shuffled. + */ + shuffle: function(t, mask) { + var _x = (mask) & 0x3; + var _y = (mask >> 2) & 0x3; + var _z = (mask >> 4) & 0x3; + var _w = (mask >> 6) & 0x3; + return new int32x4(t.storage_[_x], t.storage_[_y], t.storage_[_z], + t.storage_[_w]); + }, + /** + * @param {int32x4} t1 An instance of float32x4 to be shuffled. XY lanes in result + * @param {int32x4} t2 An instance of float32x4 to be shuffled. ZW lanes in result + * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX. + * @return {int32x4} New instance of float32x4 with lanes shuffled. + */ + shuffleMix: function(t1, t2, mask) { + var _x = (mask) & 0x3; + var _y = (mask >> 2) & 0x3; + var _z = (mask >> 4) & 0x3; + var _w = (mask >> 6) & 0x3; + return new int32x4(t1.storage_[_x], t1.storage_[_y], t2.storage_[_z], + t2.storage_[_w]); + }, + /** + * @param {float32x4} + */ + select: function(t, trueValue, falseValue) { + var tv = SIMD.float32x4.bitsToInt32x4(trueValue); + var fv = SIMD.float32x4.bitsToInt32x4(falseValue); + var tr = SIMD.int32x4.and(t, tv); + var fr = SIMD.int32x4.and(SIMD.int32x4.not(t), fv); + return SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.or(tr, fr)); + }, + /** + * @param {int32x4} t An instance of int32x4. + * @param {integer} 32-bit value used for x lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * x lane replaced with {x}. + */ + withX: function(t, x) { + return new int32x4(x, t.y, t.z, t.w); + }, + /** + * param {int32x4} t An instance of int32x4. + * @param {integer} 32-bit value used for y lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * y lane replaced with {y}. + */ + withY: function(t, y) { + return new int32x4(t.x, y, t.z, t.w); + }, + /** + * @param {int32x4} t An instance of int32x4. + * @param {integer} 32-bit value used for z lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * z lane replaced with {z}. + */ + withZ: function(t, z) { + return new int32x4(t.x, t.y, z, t.w); + }, + /** + * @param {integer} 32-bit value used for w lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * w lane replaced with {w}. + */ + withW: function(t, w) { + return new int32x4(t.x, t.y, t.z, w); + }, + /** + * @param {int32x4} t An instance of int32x4. + * @param {boolean} x flag used for x lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * x lane replaced with {x}. + */ + withFlagX: function(t, flagX) { + var x = flagX ? 0xFFFFFFFF : 0x0; + return new int32x4(x, t.y, t.z, t.w); + }, + /** + * @param {int32x4} t An instance of int32x4. + * @param {boolean} y flag used for y lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * y lane replaced with {y}. + */ + withFlagY: function(t, flagY) { + var y = flagY ? 0xFFFFFFFF : 0x0; + return new int32x4(t.x, y, t.z, t.w); + }, + /** + * @param {int32x4} t An instance of int32x4. + * @param {boolean} z flag used for z lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * z lane replaced with {z}. + */ + withFlagZ: function(t, flagZ) { + var z = flagZ ? 0xFFFFFFFF : 0x0; + return new int32x4(t.x, t.y, z, t.w); + }, + /** + * @param {int32x4} t An instance of int32x4. + * @param {boolean} w flag used for w lane. + * @return {int32x4} New instance of int32x4 with the values in t and + * w lane replaced with {w}. + */ + withFlagW: function(t, flagW) { + var w = flagW ? 0xFFFFFFFF : 0x0; + return new int32x4(t.x, t.y, t.z, w); + }, + /** + * @param {int32x4} t An instance of int32x4. + * @return {float32x4} a bit-wise copy of t as a float32x4. + */ + bitsToFloat32x4: function(t) { + var temp_storage = new Int32Array([t.storage_[0], t.storage_[1], t.storage_[2], t.storage_[3]]); + var alias = new Float32Array(temp_storage.buffer); + var fx4 = float32x4.zero(); + fx4.storage_ = alias; + return fx4; + }, + /** + * @param {int32x4} t An instance of int32x4. + * @return {float32x4} with a float to integer conversion copy of t. + */ + toFloat32x4: function(t) { + var a = float32x4.zero(); + a.storage_[0] = t.storage_[0]; + a.storage_[1] = t.storage_[1]; + a.storage_[2] = t.storage_[2]; + a.storage_[3] = t.storage_[3]; + return a; + } } } })(); @@ -955,4 +1126,3 @@ Object.defineProperty(SIMD, 'WWWX', { get: function() { return 0x3F; } }); Object.defineProperty(SIMD, 'WWWY', { get: function() { return 0x7F; } }); Object.defineProperty(SIMD, 'WWWZ', { get: function() { return 0xBF; } }); Object.defineProperty(SIMD, 'WWWW', { get: function() { return 0xFF; } }); - diff --git a/src/utility.js b/src/utility.js index cd27b20978fd6..178c596bcb803 100644 --- a/src/utility.js +++ b/src/utility.js @@ -346,13 +346,19 @@ function sortedJsonCompare(x, y) { return true; } +function escapeJSONKey(x) { + if (/^[\d\w_]+$/.exec(x) || x[0] === '"' || x[0] === "'") return x; + assert(x.indexOf("'") < 0, 'cannot have internal single quotes in keys: ' + x); + return "'" + x + "'"; +} + function stringifyWithFunctions(obj) { if (typeof obj === 'function') return obj.toString(); if (obj === null || typeof obj !== 'object') return JSON.stringify(obj); if (isArray(obj)) { return '[' + obj.map(stringifyWithFunctions).join(',') + ']'; } else { - return '{' + keys(obj).map(function(key) { return key + ':' + stringifyWithFunctions(obj[key]) }).join(',') + '}'; + return '{' + keys(obj).map(function(key) { return escapeJSONKey(key) + ':' + stringifyWithFunctions(obj[key]) }).join(',') + '}'; } } diff --git a/system/include/emscripten/emmintrin.h b/system/include/emscripten/emmintrin.h new file mode 100644 index 0000000000000..31265db803494 --- /dev/null +++ b/system/include/emscripten/emmintrin.h @@ -0,0 +1,87 @@ +#include + +typedef int32x4 __m128i; + +static __inline__ __m128i __attribute__((__always_inline__)) +_mm_set_epi32(int z, int y, int x, int w) +{ + return (__m128i){ w, x, y, z }; +} + +static __inline__ __m128i __attribute__((__always_inline__)) +_mm_set1_epi32(int w) +{ + return (__m128i){ w, w, w, w }; +} + +static __inline__ __m128i __attribute__((__always_inline__)) +_mm_setzero_si128() +{ + return (__m128i){ 0, 0, 0, 0 }; +} + +static __inline__ void __attribute__((__always_inline__)) +_mm_store_si128(__m128i *p, __m128i a) +{ + *p = a; +} + +static __inline__ __m128i __attribute__((__always_inline__)) +_mm_and_si128(__m128i a, __m128i b) +{ + return a & b; +} + +static __inline__ __m128i __attribute__((__always_inline__)) +_mm_andnot_si128(__m128i a, __m128i b) +{ + return ~a & b; +} + +static __inline__ __m128i __attribute__((__always_inline__)) +_mm_or_si128(__m128i a, __m128i b) +{ + return a | b; +} + +static __inline__ __m128i __attribute__((__always_inline__)) +_mm_xor_si128(__m128i a, __m128i b) +{ + return a ^ b; +} + +static __inline__ __m128i __attribute__((__always_inline__)) +_mm_add_epi32(__m128i a, __m128i b) +{ + return a + b; +} + +static __inline__ __m128i __attribute__((__always_inline__)) +_mm_sub_epi32(__m128i a, __m128i b) +{ + return a - b; +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_castsi128_ps(__m128i a) +{ + return emscripten_int32x4_bitsToFloat32x4(a); +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_cvtepi32_ps(__m128i a) +{ + return emscripten_int32x4_toFloat32x4(a); +} + +static __inline__ __m128i __attribute__((__always_inline__)) +_mm_castps_si128(__m128 a) +{ + return emscripten_float32x4_bitsToInt32x4(a); +} + +static __inline__ __m128i __attribute__((__always_inline__)) +_mm_cvtps_epi32(__m128 a) +{ + return emscripten_float32x4_toInt32x4(a); +} \ No newline at end of file diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h index ddcbc43a24a1c..b6e6307bf1886 100644 --- a/system/include/emscripten/emscripten.h +++ b/system/include/emscripten/emscripten.h @@ -447,6 +447,70 @@ void emscripten_asm_const(const char *code); int emscripten_asm_const_int(const char *code, ...); double emscripten_asm_const_double(const char *code, ...); +/* If specified, logs directly to the browser console/inspector + * window. If not specified, logs via the application Module. */ +#define EM_LOG_CONSOLE 1 +/* If specified, prints a warning message. */ +#define EM_LOG_WARN 2 +/* If specified, prints an error message. If neither EM_LOG_WARN + * or EM_LOG_ERROR is specified, an info message is printed. + * EM_LOG_WARN and EM_LOG_ERROR are mutually exclusive. */ +#define EM_LOG_ERROR 4 +/* If specified, prints a callstack that contains filenames referring + * to original C sources using source map information. */ +#define EM_LOG_C_STACK 8 +/* If specified, prints a callstack that contains filenames referring + * to lines to the built .js/.html file along with the message. The + * flags EM_LOG_C_STACK and EM_LOG_JS_STACK can be combined to output + * both untranslated and translated file+line information. */ +#define EM_LOG_JS_STACK 16 +/* If specified, C/C++ function names are demangled before printing. + * Otherwise, the mangled post-compilation JS function names are + * displayed. */ +#define EM_LOG_DEMANGLE 32 +/* If specified, the pathnames of the file information in the call + * stack will be omitted. */ +#define EM_LOG_NO_PATHS 64 +/* If specified, prints out the actual values of the parameters the + * functions were invoked with. */ +#define EM_LOG_FUNC_PARAMS 128 + +/* + * Prints out a message to the console, optionally with the + * callstack information. + * @param flags A binary OR of items from the list of EM_LOG_xxx + * flags that specify printing options. + * @param '...' A printf-style "format, ..." parameter list that + * is parsed according to the printf formatting rules. + */ +void emscripten_log(int flags, ...); + +/* + * Programmatically obtains the current callstack. + * @param flags A binary OR of items from the list of EM_LOG_xxx + * flags that specify printing options. The + * items EM_LOG_CONSOLE, EM_LOG_WARN and + * EM_LOG_ERROR do not apply in this function and + * are ignored. + * @param out A pointer to a memory region where the callstack + * string will be written to. The string outputted + * by this function will always be null-terminated. + * @param maxbytes The maximum number of bytes that this function can + * write to the memory pointed to by 'out'. If + * there is no enough space, the output will be + * truncated (but always null-terminated). + * @return Returns the number of bytes written. (not number of + * characters, so this will also include the terminating zero) + + * To query the amount of bytes needed for a callstack without writing + * it, pass 0 to 'out' and 'maxbytes', in which case the function will + * return the number of bytes (including the terminating zero) that + * will be needed to hold the full callstack. Note that this might be + * fully accurate since subsequent calls will carry different line + * numbers, so it is best to allocate a few bytes extra to be safe. + */ +int emscripten_get_callstack(int flags, char *out, int maxbytes); + #ifdef __cplusplus } #endif diff --git a/system/include/emscripten/vector.h b/system/include/emscripten/vector.h index 938f23692cf99..cf26a5d66fca6 100644 --- a/system/include/emscripten/vector.h +++ b/system/include/emscripten/vector.h @@ -2,7 +2,7 @@ // Support for the JS SIMD API proposal, https://github.com/johnmccutchan/ecmascript_simd typedef float float32x4 __attribute__((__vector_size__(16))); -typedef unsigned int uint32x4 __attribute__((__vector_size__(16))); +typedef unsigned int int32x4 __attribute__((__vector_size__(16))); #ifdef __cplusplus extern "C" { @@ -10,6 +10,24 @@ extern "C" { unsigned int emscripten_float32x4_signmask(float32x4 x); +float32x4 emscripten_float32x4_min(float32x4 a, float32x4 b); +float32x4 emscripten_float32x4_max(float32x4 a, float32x4 b); +float32x4 emscripten_float32x4_sqrt(float32x4 a); +float32x4 emscripten_float32x4_lessThan(float32x4 a, float32x4 b); +float32x4 emscripten_float32x4_lessThanOrEqual(float32x4 a, float32x4 b); +float32x4 emscripten_float32x4_equal(float32x4 a, float32x4 b); +float32x4 emscripten_float32x4_greaterThanOrEqual(float32x4 a, float32x4 b); +float32x4 emscripten_float32x4_greaterThan(float32x4 a, float32x4 b); +float32x4 emscripten_float32x4_and(float32x4 a, float32x4 b); +float32x4 emscripten_float32x4_andNot(float32x4 a, float32x4 b); +float32x4 emscripten_float32x4_or(float32x4 a, float32x4 b); +float32x4 emscripten_float32x4_xor(float32x4 a, float32x4 b); + +float32x4 emscripten_int32x4_bitsToFloat32x4(int32x4 a); +float32x4 emscripten_int32x4_toFloat32x4(int32x4 a); +int32x4 emscripten_float32x4_bitsToInt32x4(float32x4 a); +int32x4 emscripten_float32x4_toInt32x4(float32x4 a); + #ifdef __cplusplus } #endif diff --git a/system/include/emscripten/xmmintrin.h b/system/include/emscripten/xmmintrin.h new file mode 100644 index 0000000000000..1b9108faa7b99 --- /dev/null +++ b/system/include/emscripten/xmmintrin.h @@ -0,0 +1,131 @@ +#include + +typedef float32x4 __m128; + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_set_ps(float z, float y, float x, float w) +{ + return (__m128){ w, x, y, z }; +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_set1_ps(float w) +{ + return (__m128){ w, w, w, w }; +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_setzero_ps(void) +{ + return (__m128){ 0.0, 0.0, 0.0, 0.0 }; +} + +static __inline__ void __attribute__((__always_inline__)) +_mm_store_ps(float *p, __m128 a) +{ + *(__m128 *)p = a; +} + +static __inline__ int __attribute__((__always_inline__)) +_mm_movemask_ps(__m128 a) +{ + return emscripten_float32x4_signmask(a); +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_add_ps(__m128 a, __m128 b) +{ + return a + b; +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_sub_ps(__m128 a, __m128 b) +{ + return a - b; +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_mul_ps(__m128 a, __m128 b) +{ + return a * b; +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_div_ps(__m128 a, __m128 b) +{ + return a / b; +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_min_ps(__m128 a, __m128 b) +{ + return emscripten_float32x4_min(a, b); +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_max_ps(__m128 a, __m128 b) +{ + return emscripten_float32x4_max(a, b); +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_sqrt_ps(__m128 a) +{ + return emscripten_float32x4_sqrt(a); +} + +/* TODO: shuffles */ + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_cmplt_ps(__m128 a, __m128 b) +{ + return emscripten_float32x4_lessThan(a, b); +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_cmple_ps(__m128 a, __m128 b) +{ + return emscripten_float32x4_lessThanOrEqual(a, b); +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_cmpeq_ps(__m128 a, __m128 b) +{ + return emscripten_float32x4_equal(a, b); +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_cmpge_ps(__m128 a, __m128 b) +{ + return emscripten_float32x4_greaterThanOrEqual(a, b); +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_cmpgt_ps(__m128 a, __m128 b) +{ + return emscripten_float32x4_greaterThan(a, b); +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_and_ps(__m128 a, __m128 b) +{ + return emscripten_float32x4_and(a, b); +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_andnot_ps(__m128 a, __m128 b) +{ + return emscripten_float32x4_andNot(a, b); +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_or_ps(__m128 a, __m128 b) +{ + return emscripten_float32x4_or(a, b); +} + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_xor_ps(__m128 a, __m128 b) +{ + return emscripten_float32x4_xor(a, b); +} diff --git a/tests/cases/514_ta2.ll b/tests/cases/514_ta2.ll index ae60191c57a92..ab36324258ded 100644 --- a/tests/cases/514_ta2.ll +++ b/tests/cases/514_ta2.ll @@ -1,6 +1,6 @@ ; ModuleID = '/tmp/tmpxFUbAg/test_emcc1.bc' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" %struct.c_s = type { i8, float, i32 } diff --git a/tests/cases/aliasbitcast.ll b/tests/cases/aliasbitcast.ll index 70dc64ef9d01f..5e5f13aa453d7 100644 --- a/tests/cases/aliasbitcast.ll +++ b/tests/cases/aliasbitcast.ll @@ -1,15 +1,15 @@ ; ModuleID = '/tmp/emscripten/tmp/src.cpp.o' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" @.str = private constant [14 x i8] c"hello, world!\00", align 1 ; [#uses=1] -@_ZN16FormWidgetChoiceD2Ev = alias bitcast (i32 678 to i8*) ; [#uses=1] +@_ZN16FormWidgetChoiceD2Ev = alias i8* inttoptr (i32 678 to i8*) ; [#uses=1] ; [#uses=2] -define void @"\01_Z5hellov"() { +define void @"_Z5hellov"() { entry: - %0 = call i32 bitcast (i32 (i8*)* @puts to i32 (i32*)*)(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] + %0 = call i32 @puts(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] br label %return return: ; preds = %entry @@ -25,7 +25,7 @@ entry: %retval = alloca i32 ; [#uses=2] %0 = alloca i32 ; [#uses=2] %"alloca point" = bitcast i32 0 to i32 ; [#uses=0] - call void @"\01_Z5hellov"() + call void @"_Z5hellov"() store i32 0, i32* %0, align 4 %1 = load i32* %0, align 4 ; [#uses=1] store i32 %1, i32* %retval, align 4 diff --git a/tests/cases/atomicrmw.ll b/tests/cases/atomicrmw.ll index fe479dce038ed..31529250c8217 100644 --- a/tests/cases/atomicrmw.ll +++ b/tests/cases/atomicrmw.ll @@ -1,6 +1,6 @@ ; ModuleID = 'tests/hello_world.bc' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" @.str = private unnamed_addr constant [15 x i8] c"hello, %d,%d!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*] @@ -10,10 +10,10 @@ entry: %t = alloca i32, align 4 ; [#uses=2 type=i32**] store i32 50, i32* %t, align 4 %0 = load i32* %t - %1 = atomicrmw add i32* %t, i32 3 seq_cst, ; [#uses=0 type=i32] [debug line = 21:12] + %1 = atomicrmw add i32* %t, i32 3 seq_cst ; [#uses=0 type=i32] [debug line = 21:12] %2 = load i32* %t %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0), i32 %0, i32 %2) ; [#uses=0 type=i32] - %3 = atomicrmw volatile add i32* %t, i32 3 seq_cst, ; [#uses=0 type=i32] [debug line = 21:12] + %3 = atomicrmw volatile add i32* %t, i32 3 seq_cst ; [#uses=0 type=i32] [debug line = 21:12] ret i32 1 } diff --git a/tests/cases/atomicrmw_unaligned.ll b/tests/cases/atomicrmw_unaligned.ll index fe479dce038ed..31529250c8217 100644 --- a/tests/cases/atomicrmw_unaligned.ll +++ b/tests/cases/atomicrmw_unaligned.ll @@ -1,6 +1,6 @@ ; ModuleID = 'tests/hello_world.bc' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" @.str = private unnamed_addr constant [15 x i8] c"hello, %d,%d!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*] @@ -10,10 +10,10 @@ entry: %t = alloca i32, align 4 ; [#uses=2 type=i32**] store i32 50, i32* %t, align 4 %0 = load i32* %t - %1 = atomicrmw add i32* %t, i32 3 seq_cst, ; [#uses=0 type=i32] [debug line = 21:12] + %1 = atomicrmw add i32* %t, i32 3 seq_cst ; [#uses=0 type=i32] [debug line = 21:12] %2 = load i32* %t %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0), i32 %0, i32 %2) ; [#uses=0 type=i32] - %3 = atomicrmw volatile add i32* %t, i32 3 seq_cst, ; [#uses=0 type=i32] [debug line = 21:12] + %3 = atomicrmw volatile add i32* %t, i32 3 seq_cst ; [#uses=0 type=i32] [debug line = 21:12] ret i32 1 } diff --git a/tests/cases/breakinthemiddle.ll b/tests/cases/breakinthemiddle.ll index 769b0e11968bd..6e05b85387f65 100644 --- a/tests/cases/breakinthemiddle.ll +++ b/tests/cases/breakinthemiddle.ll @@ -1,8 +1,11 @@ +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" + @.str = private constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1] -define linkonce_odr i32* @main() align 2 { - %199 = trunc i8 1 to i1 ; [#uses=1] - br i1 %199, label %label555, label %label569 +define linkonce_odr i32 @main() align 2 { + %1 = trunc i8 1 to i1 ; [#uses=1] + br i1 %1, label %label555, label %label569 label555: ; preds = %353 br label %label569 @@ -10,7 +13,7 @@ label555: ; preds = %353 br label %label569 label569: ; preds = %555 - %333 = call i32 @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] + %3 = call i32 @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] ret i32 0 } diff --git a/tests/cases/breakinthemiddle2.ll b/tests/cases/breakinthemiddle2.ll index 318b49dc6d6da..ba96654f66f7b 100644 --- a/tests/cases/breakinthemiddle2.ll +++ b/tests/cases/breakinthemiddle2.ll @@ -1,21 +1,24 @@ +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" + @.str = private constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1] define linkonce_odr i32 @main() align 2 { - %333 = call i32 @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] - %199 = trunc i8 1 to i1 ; [#uses=1] - br i1 %199, label %label555, label %label569 + %a333 = call i32 @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] + %b199 = trunc i8 1 to i1 ; [#uses=1] + br i1 %b199, label %label555, label %label569 label555: ; preds = %0 br label %label569 ; branch should ignore all code after it in the block ; No predecessors! - %a472 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) + %aa472 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) cleanup - %a473 = extractvalue { i8*, i32 } %a472, 0 - %a474 = extractvalue { i8*, i32 } %a472, 1 + %aa473 = extractvalue { i8*, i32 } %aa472, 0 + %aa474 = extractvalue { i8*, i32 } %aa472, 1 br label %label569 label569: ; preds = %0 - br i1 %199, label %label990, label %label999 + br i1 %b199, label %label990, label %label999 label990: ret i32 0 ; ret should ignore all code after it in the block diff --git a/tests/cases/breakinthemiddle3.ll b/tests/cases/breakinthemiddle3.ll index e9173965f4ca4..38da15efa5056 100644 --- a/tests/cases/breakinthemiddle3.ll +++ b/tests/cases/breakinthemiddle3.ll @@ -1,9 +1,12 @@ +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" + @.str = private constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1] define linkonce_odr i32 @main() align 2 { - %333 = call i32 @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] - %199 = trunc i8 1 to i1 ; [#uses=1] - switch i32 %333, label %label999 [ + %a333 = call i32 @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] + %z199 = trunc i8 1 to i1 ; [#uses=1] + switch i32 %a333, label %label999 [ i32 1000, label %label995 ] ; switch should ignore all code after it in the block ; No predecessors! @@ -14,7 +17,7 @@ define linkonce_odr i32 @main() align 2 { br label %label999 label995: - %333b = call i32 @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] + %b333b = call i32 @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] br label %label999 label999: ; preds = %555 diff --git a/tests/cases/caall.ll b/tests/cases/caall.ll index 5b8f7f29ad853..2cc231ecff4a1 100644 --- a/tests/cases/caall.ll +++ b/tests/cases/caall.ll @@ -1,6 +1,6 @@ ; ModuleID = 'tests/hello_world.bc' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" @.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*] @@ -11,14 +11,14 @@ entry: store i32 0, i32* %retval %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32] %call12 = call void (i32*)** @_ZNSt3__13mapINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPFvP6ObjectENS_4lessIS6_EENS4_INS_4pairIKS6_SA_EEEEEixERSE_(i32 10) - %26 = load void (%class.Object*)** %call12 + %l26 = load void (i32*)** %call12 ret i32 1 } -define (i32*)** @_ZNSt3__13mapINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPFvP6ObjectENS_4lessIS6_EENS4_INS_4pairIKS6_SA_EEEEEixERSE_(i32 %x) { +define void (i32*)** @_ZNSt3__13mapINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPFvP6ObjectENS_4lessIS6_EENS4_INS_4pairIKS6_SA_EEEEEixERSE_(i32 %x) { entry: - %ret = inttoptr i32 0 to (i32*)** - ret (i32*)** %ret + %ret = inttoptr i32 0 to void (i32*)** + ret void (i32*)** %ret } ; [#uses=1] diff --git a/tests/cases/complexphi.ll b/tests/cases/complexphi.ll index fcb7185fce3ce..e79e6f1b33574 100644 --- a/tests/cases/complexphi.ll +++ b/tests/cases/complexphi.ll @@ -1,6 +1,6 @@ ; ModuleID = '/dev/shm/tmp/src.cpp.o' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" @.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*] @_dispatchTable = internal global i64 0 @@ -21,8 +21,6 @@ cond.end: ; preds = %cond.false, %cond.t %cond = phi { i32, i32 } [ { i32 5, i32 6 }, %entry ], [ zeroinitializer, %cond.null ] ; [#uses=1] store { i32, i32 } %cond, { i32, i32 }* %comp - store { i32, i32 } { i32 ptrtoint (i64* @_dispatchTable to i32), i32 0 }, { i32, i32 }* getelementptr inbounds ([1 x i64]* @_dispatchTable, i32 0, i32 0, i32 1), align 4 - ret i32 0 ; [debug line = 6:13] } diff --git a/tests/cases/dash.ll b/tests/cases/dash.ll index ed5b01aefb02f..6833a42e3304c 100644 --- a/tests/cases/dash.ll +++ b/tests/cases/dash.ll @@ -1,6 +1,6 @@ ; ModuleID = '/tmp/tmpqfApGD/a.out.bc' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" @other-name = alias i32 ()* @main @@ -10,9 +10,9 @@ define i32 @main() { entry: %ret-val = alloca i32, align 4 store i32 0, i32* %ret-val - %aaa = bitcast i32 ()* @other-name to i32 + %aaa = ptrtoint i32 ()* @other-name to i32 %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.st-r, i32 0, i32 0), i32 %aaa) - ret i32 %ret-val + ret i32 0 } declare i32 @printf(i8*, ...) diff --git a/tests/cases/emptyalloca.ll b/tests/cases/emptyalloca.ll new file mode 100644 index 0000000000000..f12a416185e9b --- /dev/null +++ b/tests/cases/emptyalloca.ll @@ -0,0 +1,31 @@ +; ModuleID = '/tmp/tmpjSNiky/a.out.bc' +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" + +@.str = private unnamed_addr constant [30 x i8] c"Module.print('hello, world!')\00", align 1 + +; Function Attrs: nounwind +define internal void @_Z9doNothingPi(i32* %arr) #0 { + %1 = alloca i32*, align 4 + store i32* %arr, i32** %1, align 4 + ret void +} + +define i32 @main() #1 { + %arr = alloca [0 x i32], align 4 + %1 = bitcast [0 x i32]* %arr to i8* + call void @llvm.memset.p0i8.i32(i8* %1, i8 0, i32 0, i32 4, i1 false) + %2 = getelementptr inbounds [0 x i32]* %arr, i32 0, i32 0 + call void @_Z9doNothingPi(i32* %2) + call void @emscripten_asm_const(i8* getelementptr inbounds ([30 x i8]* @.str, i32 0, i32 0)) + ret i32 0 +} + +; Function Attrs: nounwind +declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1) #2 + +declare void @emscripten_asm_const(i8*) #1 + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind } diff --git a/tests/cases/emptyasm_le32.ll b/tests/cases/emptyasm_le32.ll index e123d3d52ef3b..8f6b606e10a22 100644 --- a/tests/cases/emptyasm_le32.ll +++ b/tests/cases/emptyasm_le32.ll @@ -1,3 +1,6 @@ +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" + ; ModuleID = 'tests/hello_world.bc' @.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*] @@ -7,7 +10,7 @@ define i32 @main() { entry: %retval = alloca i32, align 4 ; [#uses=1 type=i32*] store i32 0, i32* %retval - call void asm sideeffect "", "~{memory}"() nounwind, !srcloc !0 + call void asm sideeffect "", "~{memory}"() nounwind %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32] ret i32 1 } diff --git a/tests/cases/entry3.ll b/tests/cases/entry3.ll index a20c684383584..6888d0a8f2b11 100644 --- a/tests/cases/entry3.ll +++ b/tests/cases/entry3.ll @@ -1,25 +1,25 @@ ; ModuleID = '/tmp/tmpKnA2D3/a.out.bc' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" @.str = private unnamed_addr constant [11 x i8] c"getgid=%d\0A\00", align 1 @.str1 = private unnamed_addr constant [6 x i8] c"f=%d\0A\00", align 1 define internal i32 @_Z1fii(i32, i32) noinline { entry: - %3 = tail call i32 @getgid() - %4 = icmp eq i32 %3, 0 - br i1 %4, label %cond.b, label %cond.a + %a3 = tail call i32 @getgid() + %a4 = icmp eq i32 %a3, 0 + br i1 %a4, label %cond.b, label %cond.a cond.a: - %6 = tail call i32 @getgid() + %a6 = tail call i32 @getgid() br label %cond.end cond.b: br label %cond.end cond.end: - %.0 = phi i32 [ 0, %cond.b ], [ 1, %1 ] + %.0 = phi i32 [ 0, %cond.b ], [ 1, %cond.a ] ret i32 %.0 } diff --git a/tests/cases/funcptr.ll b/tests/cases/funcptr.ll index 0aa03fcf99f56..ef869c3324382 100644 --- a/tests/cases/funcptr.ll +++ b/tests/cases/funcptr.ll @@ -1,6 +1,6 @@ ; ModuleID = 'tests/hello_world.bc' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" @.str = private unnamed_addr constant [17 x i8] c"hello %d world!\0A\00", align 1 ; [#uses=1 type=[17 x i8]*] @@ -9,7 +9,7 @@ define i32 @main() { entry: %retval = alloca i32, align 4 ; [#uses=1 type=i32*] store i32 0, i32* %retval - %access_virt_barray = bitcast i32 100 to [64 x i16]* (i32*, i32)** + %access_virt_barray = inttoptr i32 100 to [64 x i16]* (i32*, i32)** store [64 x i16]* (i32*, i32)* @access_virt_barray, [64 x i16]* (i32*, i32)** %access_virt_barray, align 4 %wakaptr = bitcast [64 x i16]* (i32*, i32)** %access_virt_barray to i32* %waka = load i32* %wakaptr @@ -20,7 +20,7 @@ entry: } define [64 x i16]* @access_virt_barray(i32*, i32) { - ret void + ret [64 x i16]* inttoptr (i32 0 to [64 x i16]*) } ; [#uses=1] diff --git a/tests/cases/i24_mem_ta2.ll b/tests/cases/i24_mem_ta2.ll index e50014ca156c1..550389fe9979f 100644 --- a/tests/cases/i24_mem_ta2.ll +++ b/tests/cases/i24_mem_ta2.ll @@ -1,8 +1,8 @@ ; ModuleID = 'tests/hello_world.bc' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" -@.str = private unnamed_addr constant [15 x i8] c".%x.\0A\00", align 1 ; [#uses=1 type=[5 x i8]*] +@.str = private unnamed_addr constant [6 x i8] c".%x.\0A\00", align 1 ; [#uses=1 type=[5 x i8]*] define i32 @main() { entry: @@ -11,11 +11,11 @@ entry: %i24 = bitcast i32* %mem to i24* %load = load i24* %i24, align 4 %load32 = zext i24 %load to i32 - %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([5 x i8]* @.str, i32 0, i32 0), i32 %load32) + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0), i32 %load32) %val_24 = trunc i32 4041265344 to i24 store i24 %val_24, i24* %i24, align 4 %load32b = load i32* %mem, align 4 - %call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([5 x i8]* @.str, i32 0, i32 0), i32 %load32b) + %call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0), i32 %load32b) ret i32 1 } diff --git a/tests/cases/i64toi8star.ll b/tests/cases/i64toi8star.ll index d4a39340445ca..b23074491c7c9 100644 --- a/tests/cases/i64toi8star.ll +++ b/tests/cases/i64toi8star.ll @@ -25,8 +25,8 @@ entry: %retval = alloca i32 ; [#uses=2] %0 = alloca i32 ; [#uses=2] %"alloca point" = bitcast i32 0 to i32 ; [#uses=0] - %5 = call i32 @PyLong_FromVoidPtr(i8* null) nounwind ; [#uses=0] - %13 = call i32 @PyLong_FromVoidPtr(i8* inttoptr (i64 1 to i8*)) nounwind ; [#uses=0] - %1 = call i32 bitcast (i32 (i8*)* @puts to i32 (i32*)*)(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] + %a5 = call i32 @PyLong_FromVoidPtr(i8* null) nounwind ; [#uses=0] + %a13 = call i32 @PyLong_FromVoidPtr(i8* inttoptr (i64 1 to i8*)) nounwind ; [#uses=0] + %a1 = call i32 @puts(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] ret i32 0 } diff --git a/tests/cases/inttoptr.ll b/tests/cases/inttoptr.ll index b0711672c9778..c1b40a74e4af0 100644 --- a/tests/cases/inttoptr.ll +++ b/tests/cases/inttoptr.ll @@ -1,6 +1,6 @@ ; ModuleID = '/tmp/emscripten/tmp/src.cpp.o' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" @.str = private constant [14 x i8] c"hello, world!\00", align 1 ; [#uses=1] @@ -14,7 +14,7 @@ entry: %0 = alloca i32 ; [#uses=2] %"alloca point" = bitcast i32 0 to i32 ; [#uses=0] %sz.i7 = inttoptr i32 64 to i32* ; [#uses=1 type=i32*] - store i32 184, i32* %sz.i7, align 8, !tbaa !1610 - %1 = call i32 bitcast (i32 (i8*)* @puts to i32 (i32*)*)(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] + store i32 184, i32* %sz.i7, align 8 + %1 = call i32 @puts(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] ret i32 0 } diff --git a/tests/cases/invokebitcast.ll b/tests/cases/invokebitcast.ll index ffb5803f12da6..ec090b0dd87a0 100644 --- a/tests/cases/invokebitcast.ll +++ b/tests/cases/invokebitcast.ll @@ -1,7 +1,7 @@ ; ModuleID = '/dev/shm/tmp/src.cpp.o' ; Just test for compilation here -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-f128:128:128-n8:16:32" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" %struct.CPU_Regs = type { [8 x %union.GenReg32] } %union.GenReg32 = type { [1 x i32] } @@ -16,7 +16,8 @@ entry: %0 = alloca i32 ; [#uses=2] %"alloca point" = bitcast i32 0 to i32 ; [#uses=0] %1 = load i32* bitcast (i32* getelementptr inbounds (%struct.CPU_Regs* @cpu_regs, i32 0, i32 0, i32 1, i32 0, i32 0) to i32*), align 2 ; [#uses=1] - store i16 %1, i16* bitcast (%struct.CPU_Regs* @cpu_regs to i16*), align 2 + %s = trunc i32 %1 to i16 + store i16 %s, i16* bitcast (%struct.CPU_Regs* @cpu_regs to i16*), align 2 %2 = call i32 @puts(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] store i32 0, i32* %0, align 4 %3 = load i32* %0, align 4 ; [#uses=1] diff --git a/tests/cases/invokeundef.ll b/tests/cases/invokeundef.ll index 9dc1f93dcc30a..be1dd67196fa8 100644 --- a/tests/cases/invokeundef.ll +++ b/tests/cases/invokeundef.ll @@ -1,7 +1,7 @@ ; ModuleID = '/dev/shm/tmp/src.cpp.o' ; Just test for compilation here -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-f128:128:128-n8:16:32" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" %struct.CPU_Regs = type { [8 x %union.GenReg32] } %union.GenReg32 = type { [1 x i32] } @@ -16,14 +16,15 @@ entry: %0 = alloca i32 ; [#uses=2] %"alloca point" = bitcast i32 0 to i32 ; [#uses=0] %1 = load i32* bitcast (i32* getelementptr inbounds (%struct.CPU_Regs* @cpu_regs, i32 0, i32 0, i32 1, i32 0, i32 0) to i32*), align 2 ; [#uses=1] - store i16 %1, i16* bitcast (%struct.CPU_Regs* @cpu_regs to i16*), align 2 + %a1 = trunc i32 %1 to i16 + store i16 %a1, i16* bitcast (%struct.CPU_Regs* @cpu_regs to i16*), align 2 %2 = call i32 @puts(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] store i32 0, i32* %0, align 4 %3 = load i32* %0, align 4 ; [#uses=1] store i32 %3, i32* %retval, align 4 br label %return - invoke void undef(%struct.CPU_Regs* noalias @cpu_regs, i32 %99) + invoke void undef(%struct.CPU_Regs* noalias @cpu_regs, i32 0) to label %invcont33 unwind label %lpad106 invcont33: diff --git a/tests/cases/legalizer_ta2.ll b/tests/cases/legalizer_ta2.ll index 89ebcef6056d6..6f153ad2416f1 100644 --- a/tests/cases/legalizer_ta2.ll +++ b/tests/cases/legalizer_ta2.ll @@ -1,6 +1,6 @@ ; ModuleID = 'tests/hello_world.bc' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" @globaliz = global [300 x i8] zeroinitializer diff --git a/tests/cases/loadbitcastgep.ll b/tests/cases/loadbitcastgep.ll index 2a90dbb7f8f36..cfb88a0d97b6a 100644 --- a/tests/cases/loadbitcastgep.ll +++ b/tests/cases/loadbitcastgep.ll @@ -1,6 +1,6 @@ ; ModuleID = '/dev/shm/tmp/src.cpp.o' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-f128:128:128-n8:16:32" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" %struct.CPU_Regs = type { [8 x %union.GenReg32] } %union.GenReg32 = type { [1 x i32] } @@ -15,7 +15,8 @@ entry: %0 = alloca i32 ; [#uses=2] %"alloca point" = bitcast i32 0 to i32 ; [#uses=0] %1 = load i32* bitcast (i32* getelementptr inbounds (%struct.CPU_Regs* @cpu_regs, i32 0, i32 0, i32 1, i32 0, i32 0) to i32*), align 2 ; [#uses=1] - store i16 %1, i16* bitcast (%struct.CPU_Regs* @cpu_regs to i16*), align 2 + %b = trunc i32 %1 to i16 + store i16 %b, i16* bitcast (%struct.CPU_Regs* @cpu_regs to i16*), align 2 %2 = call i32 @puts(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] store i32 0, i32* %0, align 4 %3 = load i32* %0, align 4 ; [#uses=1] diff --git a/tests/cases/longjmp_tiny_invoke_phi.ll b/tests/cases/longjmp_tiny_invoke_phi.ll new file mode 100644 index 0000000000000..30c4333918909 --- /dev/null +++ b/tests/cases/longjmp_tiny_invoke_phi.ll @@ -0,0 +1,46 @@ +; ModuleID = '/tmp/emscripten_temp/src.cpp.o' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" +target triple = "i386-pc-linux-gnu" + +@_ZL3buf = internal global [20 x i16] zeroinitializer, align 2 +@.str = private unnamed_addr constant [13 x i8] c"hello world\0A\00", align 1 +@.str1 = private unnamed_addr constant [6 x i8] c"more\0A\00", align 1 +@.str2 = private unnamed_addr constant [6 x i8] c"fair\0A\00", align 1 + +define i32 @main() { +entry: + %retval = alloca i32, align 4 + store i32 0, i32* %retval + %call = invoke i32 @setjmp(i16* getelementptr inbounds ([20 x i16]* @_ZL3buf, i32 0, i32 0)) returns_twice + to label %allgood unwind label %awful + +allgood: + %p = phi i32 [0, %entry], [1, %if.else] + %calll = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str2, i32 0, i32 0)) + %total = add i32 %p, %call + %tobool = icmp ne i32 %total, 10 + br i1 %tobool, label %if.then, label %if.else + +if.then: ; preds = %entry + %call1 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([13 x i8]* @.str, i32 0, i32 0)) + call void @longjmp(i16* getelementptr inbounds ([20 x i16]* @_ZL3buf, i32 0, i32 0), i32 10) + br label %if.end + +if.else: ; preds = %entry + %call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str1, i32 0, i32 0)) + %chak = icmp ne i32 %call2, 1337 + br i1 %chak, label %if.end, label %allgood + +if.end: ; preds = %if.else, %if.then + ret i32 0 + +awful: + ret i32 1 +} + +declare i32 @setjmp(i16*) returns_twice + +declare i32 @printf(i8*, ...) + +declare void @longjmp(i16*, i32) + diff --git a/tests/cases/longjmp_tiny_invoke_phi.txt b/tests/cases/longjmp_tiny_invoke_phi.txt new file mode 100644 index 0000000000000..aaa41d119eb28 --- /dev/null +++ b/tests/cases/longjmp_tiny_invoke_phi.txt @@ -0,0 +1,4 @@ +fair +hello world +fair +more diff --git a/tests/cases/oob_ta2.ll b/tests/cases/oob_ta2.ll index 3c94c13cb5a94..b95d28dac80ea 100644 --- a/tests/cases/oob_ta2.ll +++ b/tests/cases/oob_ta2.ll @@ -1,6 +1,6 @@ ; ModuleID = 'tests/hello_world.bc' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" %structy = type { [2 x [10 x i8]] } diff --git a/tests/cases/phi24_ta2.ll b/tests/cases/phi24_ta2.ll index 2d9b664629893..18577fee6cedc 100644 --- a/tests/cases/phi24_ta2.ll +++ b/tests/cases/phi24_ta2.ll @@ -1,9 +1,6 @@ -;;; trunc i32 into i24, needs $0 on target variable name - -; ModuleID = '/tmp/tmpvqlBv2/a.out.bc' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" %union.U4 = type { i32 } %union.U3 = type { i8* } diff --git a/tests/cases/phicubed.ll b/tests/cases/phicubed.ll index a079999741bb9..5fc3208bdbbfd 100644 --- a/tests/cases/phicubed.ll +++ b/tests/cases/phicubed.ll @@ -1,4 +1,6 @@ ; ModuleID = '/dev/shm/tmp/src.cpp.o' +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" %struct.worker_args = type { i32, %struct.worker_args* } diff --git a/tests/cases/phientryimplicit.ll b/tests/cases/phientryimplicit.ll index 8a510f43c914c..c237457cc1ed5 100644 --- a/tests/cases/phientryimplicit.ll +++ b/tests/cases/phientryimplicit.ll @@ -1,6 +1,6 @@ ; ModuleID = 'tests/hello_world.bc' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" -target triple = "i386-pc-linux-gnu" +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" ; Phi nodes can refer to the entry. And the entry might be unnamed, and doesn't even have a consistent implicit name! @@ -9,35 +9,35 @@ target triple = "i386-pc-linux-gnu" ; [#uses=0] define i32 @main() { %retval = alloca i32, align 4 ; [#uses=1 type=i32*] - %16 = trunc i32 1 to i1 - br i1 %16, label %17, label %26, !dbg !1269853 ; [debug line = 3920:5] + %a16 = trunc i32 1 to i1 + br i1 %a16, label %L17, label %L26, !dbg !1269853 ; [debug line = 3920:5] -;