aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/js-optimizer.js18
-rw-r--r--tools/shared.py112
-rw-r--r--tools/test-js-optimizer-asm-outline1-output.js133
-rw-r--r--tools/test-js-optimizer-asm-outline1.js40
-rw-r--r--tools/test-js-optimizer-regs-output.js16
-rw-r--r--tools/test-js-optimizer-regs.js2
-rw-r--r--tools/validate_asmjs.py82
7 files changed, 351 insertions, 52 deletions
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 788a76ed..e567ebff 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -1728,6 +1728,15 @@ function getStackBumpSize(ast) {
function registerize(ast) {
traverseGeneratedFunctions(ast, function(fun) {
if (asm) var asmData = normalizeAsm(fun);
+ if (!asm) {
+ var hasFunction = false;
+ traverse(fun, function(node, type) {
+ if (type === 'function') hasFunction = true;
+ });
+ if (hasFunction) {
+ return; // inline assembly, and not asm (where we protect it in normalize/denormalize), so abort registerize pass
+ }
+ }
// Add parameters as a first (fake) var (with assignment), so they get taken into consideration
var params = {}; // note: params are special, they can never share a register between them (see later)
if (fun[2] && fun[2].length) {
@@ -3032,6 +3041,9 @@ function outline(ast) {
}
var ignore = [];
traverse(func, function(node) {
+ if (node[0] === 'while' && node[2][0] !== 'block') {
+ node[2] = ['block', [node[2]]]; // so we have a list of statements and can flatten while(1) switch
+ }
var stats = getStatements(node);
if (stats) {
for (var i = 0; i < stats.length; i++) {
@@ -3708,8 +3720,10 @@ function outline(ast) {
}
}
}
- ret.push(func);
- printErr('... resulting sizes of ' + func[1] + ' is ' + ret.map(measureSize) + '\n');
+ if (ret) {
+ ret.push(func);
+ printErr('... resulting sizes of ' + func[1] + ' is ' + ret.map(measureSize) + '\n');
+ }
}
denormalizeAsm(func, asmData);
});
diff --git a/tools/shared.py b/tools/shared.py
index 2e11d736..94daadae 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -546,15 +546,13 @@ def get_llvm_target():
return os.environ.get('EMCC_LLVM_TARGET') or 'le32-unknown-nacl' # 'i386-pc-linux-gnu'
LLVM_TARGET = get_llvm_target()
+# COMPILER_OPTS: options passed to clang when generating bitcode for us
try:
COMPILER_OPTS # Can be set in EM_CONFIG, optionally
except:
COMPILER_OPTS = []
-# Force a simple, standard target as much as possible: target 32-bit linux, and disable various flags that hint at other platforms
-COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-U__i386__', '-U__i386', '-Ui386',
- '-U__SSE__', '-U__SSE_MATH__', '-U__SSE2__', '-U__SSE2_MATH__', '-U__MMX__',
- '-DEMSCRIPTEN', '-D__EMSCRIPTEN__', '-U__STRICT_ANSI__',
- '-D__IEEE_LITTLE_ENDIAN', '-fno-math-errno',
+COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-DEMSCRIPTEN', '-D__EMSCRIPTEN__',
+ '-fno-math-errno',
#'-fno-threadsafe-statics', # disabled due to issue 1289
'-target', LLVM_TARGET]
@@ -562,6 +560,11 @@ if LLVM_TARGET == 'le32-unknown-nacl':
COMPILER_OPTS = filter(lambda opt: opt != '-m32', COMPILER_OPTS) # le32 target is 32-bit anyhow, no need for -m32
COMPILER_OPTS += ['-U__native_client__', '-U__pnacl__', '-U__ELF__'] # The nacl target is originally used for Google Native Client. Emscripten is not NaCl, so remove the platform #define, when using their triple.
+# Remove various platform specific defines, and set little endian
+COMPILER_STANDARDIZATION_OPTS = ['-U__i386__', '-U__i386', '-Ui386', '-U__STRICT_ANSI__', '-D__IEEE_LITTLE_ENDIAN',
+ '-U__SSE__', '-U__SSE_MATH__', '-U__SSE2__', '-U__SSE2_MATH__', '-U__MMX__',
+ '-U__APPLE__', '-U__linux__']
+
USE_EMSDK = not os.environ.get('EMMAKEN_NO_SDK')
if USE_EMSDK:
@@ -578,9 +581,8 @@ if USE_EMSDK:
'-Xclang', '-isystem' + path_from_root('system', 'include', 'gfx'),
'-Xclang', '-isystem' + path_from_root('system', 'include', 'net'),
'-Xclang', '-isystem' + path_from_root('system', 'include', 'SDL'),
- ] + [
- '-U__APPLE__', '-U__linux__'
]
+ EMSDK_OPTS += COMPILER_STANDARDIZATION_OPTS
if LLVM_TARGET != 'le32-unknown-nacl':
EMSDK_CXX_OPTS = ['-nostdinc++'] # le32 target does not need -nostdinc++
else:
@@ -589,6 +591,7 @@ if USE_EMSDK:
else:
EMSDK_OPTS = []
EMSDK_CXX_OPTS = []
+ COMPILER_OPTS += COMPILER_STANDARDIZATION_OPTS
#print >> sys.stderr, 'SDK opts', ' '.join(EMSDK_OPTS)
#print >> sys.stderr, 'Compiler opts', ' '.join(COMPILER_OPTS)
@@ -817,35 +820,52 @@ class Building:
env['EMSCRIPTEN'] = path_from_root()
return env
+ # Finds the given executable 'program' in PATH. Operates like the Unix tool 'which'.
+ @staticmethod
+ def which(program):
+ import os
+ 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
+
@staticmethod
def handle_CMake_toolchain(args, env):
- CMakeToolchain = ('''# the name of the target operating system
-SET(CMAKE_SYSTEM_NAME Linux)
-
-# which C and C++ compiler to use
-SET(CMAKE_C_COMPILER %(winfix)s$EMSCRIPTEN_ROOT/emcc)
-SET(CMAKE_CXX_COMPILER %(winfix)s$EMSCRIPTEN_ROOT/em++)
-SET(CMAKE_AR %(winfix)s$EMSCRIPTEN_ROOT/emar)
-SET(CMAKE_RANLIB %(winfix)s$EMSCRIPTEN_ROOT/emranlib)
-SET(CMAKE_C_FLAGS $CFLAGS)
-SET(CMAKE_CXX_FLAGS $CXXFLAGS)
-
-# here is the target environment located
-SET(CMAKE_FIND_ROOT_PATH $EMSCRIPTEN_ROOT/system/include )
-
-# adjust the default behaviour of the FIND_XXX() commands:
-# search headers and libraries in the target environment, search
-# programs in the host environment
-set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)
-set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS else 'python ' }) \
- .replace('$EMSCRIPTEN_ROOT', path_from_root('').replace('\\', '/')) \
- .replace('$CFLAGS', env['CFLAGS']) \
- .replace('$CXXFLAGS', env['CFLAGS'])
- toolchainFile = mkstemp(suffix='.cmaketoolchain.txt', dir=configuration.TEMP_DIR)[1]
- open(toolchainFile, 'w').write(CMakeToolchain)
- args.append('-DCMAKE_TOOLCHAIN_FILE=%s' % os.path.abspath(toolchainFile))
+
+ def has_substr(array, substr):
+ for arg in array:
+ if substr in arg:
+ return True
+ return False
+
+ # Append the Emscripten toolchain file if the user didn't specify one.
+ if not has_substr(args, '-DCMAKE_TOOLCHAIN_FILE'):
+ args.append('-DCMAKE_TOOLCHAIN_FILE=' + path_from_root('cmake', 'Platform', 'Emscripten.cmake'))
+
+ # On Windows specify MinGW Makefiles if we have MinGW and no other toolchain was specified, to avoid CMake
+ # pulling in a native Visual Studio, or Unix Makefiles.
+ if WINDOWS and not '-G' in args and Building.which('mingw32-make'):
+ args += ['-G', 'MinGW Makefiles']
+
return args
@staticmethod
@@ -876,6 +896,13 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
logging.error('Executable to run not specified.')
sys.exit(1)
#args += ['VERBOSE=1']
+
+ # On Windows prefer building with mingw32-make instead of make, if it exists.
+ if WINDOWS and args[0] == 'make':
+ mingw32_make = Building.which('mingw32-make')
+ if mingw32_make:
+ args[0] = mingw32_make
+
try:
process = Popen(args, stdout=stdout, stderr=stderr, env=env)
process.communicate()
@@ -916,13 +943,13 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
# except:
# pass
env = Building.get_building_env(native)
- log_to_file = os.getenv('EM_BUILD_VERBOSE') == None or int(os.getenv('EM_BUILD_VERBOSE')) == 0
+ verbose_level = int(os.getenv('EM_BUILD_VERBOSE')) if os.getenv('EM_BUILD_VERBOSE') != None else 0
for k, v in env_init.iteritems():
env[k] = v
if configure: # Useful in debugging sometimes to comment this out (and the lines below up to and including the |link| call)
try:
- Building.configure(configure + configure_args, env=env, stdout=open(os.path.join(project_dir, 'configure_'), 'w') if log_to_file else None,
- stderr=open(os.path.join(project_dir, 'configure_err'), 'w') if log_to_file else None)
+ Building.configure(configure + configure_args, env=env, stdout=open(os.path.join(project_dir, 'configure_'), 'w') if verbose_level < 2 else None,
+ stderr=open(os.path.join(project_dir, 'configure_err'), 'w') if verbose_level < 1 else None)
except subprocess.CalledProcessError, e:
pass # Ignore exit code != 0
def open_make_out(i, mode='r'):
@@ -930,13 +957,16 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
def open_make_err(i, mode='r'):
return open(os.path.join(project_dir, 'make_err' + str(i)), mode)
-
+
+ if verbose_level >= 3:
+ make_args += ['VERBOSE=1']
+
for i in range(2): # FIXME: Sad workaround for some build systems that need to be run twice to succeed (e.g. poppler)
with open_make_out(i, 'w') as make_out:
with open_make_err(i, 'w') as make_err:
try:
- Building.make(make + make_args, stdout=make_out if log_to_file else None,
- stderr=make_err if log_to_file else None, env=env)
+ Building.make(make + make_args, stdout=make_out if verbose_level < 2 else None,
+ stderr=make_err if verbose_level < 1 else None, env=env)
except subprocess.CalledProcessError, e:
pass # Ignore exit code != 0
try:
@@ -948,7 +978,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
break
except Exception, e:
if i > 0:
- if log_to_file:
+ if verbose_level == 0:
# Due to the ugly hack above our best guess is to output the first run
with open_make_err(0) as ferr:
for line in ferr:
diff --git a/tools/test-js-optimizer-asm-outline1-output.js b/tools/test-js-optimizer-asm-outline1-output.js
index 612da16a..27f93d8a 100644
--- a/tools/test-js-optimizer-asm-outline1-output.js
+++ b/tools/test-js-optimizer-asm-outline1-output.js
@@ -372,6 +372,57 @@ function switchh() {
}
STACKTOP = sp;
}
+function switchh2() {
+ var helper$0 = 0, helper$1 = 0, sp = 0;
+ sp = STACKTOP;
+ STACKTOP = STACKTOP + 280 | 0;
+ while (1) {
+ helper$0 = 1;
+ helper$1 = x;
+ if (helper$0) {
+ helper$0 = 0;
+ switch (helper$1 | 0) {
+ case 0:
+ f(0);
+ g();
+ break;
+ default:
+ {
+ helper$0 = 1;
+ }
+ }
+ }
+ HEAP32[sp + 8 >> 2] = helper$0;
+ HEAP32[sp + 16 >> 2] = helper$1;
+ HEAP32[sp + 40 >> 2] = 0;
+ HEAP32[sp + 44 >> 2] = 0;
+ switchh2$2(sp);
+ helper$0 = HEAP32[sp + 8 >> 2] | 0;
+ tempValue = HEAP32[sp + 40 >> 2] | 0;
+ tempInt = HEAP32[sp + 44 >> 2] | 0;
+ tempDouble = +HEAPF32[sp + 44 >> 2];
+ HEAP32[sp + 40 >> 2] = 0;
+ HEAP32[sp + 44 >> 2] = 0;
+ if ((tempValue | 0) == 5) {
+ STACKTOP = sp;
+ return;
+ }
+ HEAP32[sp + 8 >> 2] = helper$0;
+ HEAP32[sp + 16 >> 2] = helper$1;
+ HEAP32[sp + 32 >> 2] = 0;
+ HEAP32[sp + 36 >> 2] = 0;
+ switchh2$1(sp);
+ helper$0 = HEAP32[sp + 8 >> 2] | 0;
+ if (helper$0) {
+ helper$0 = 0;
+ HEAP32[sp + 16 >> 2] = helper$1;
+ HEAP32[sp + 24 >> 2] = 0;
+ HEAP32[sp + 28 >> 2] = 0;
+ switchh2$0(sp);
+ }
+ }
+ STACKTOP = sp;
+}
function lin$0(sp) {
sp = sp | 0;
c(14);
@@ -793,4 +844,86 @@ function switchh$2(sp) {
} while (0);
HEAP32[sp + 8 >> 2] = helper$0;
}
+function switchh2$0(sp) {
+ sp = sp | 0;
+ var helper$1 = 0;
+ helper$1 = HEAP32[sp + 16 >> 2] | 0;
+ switch (helper$1 | 0) {
+ case 4:
+ f(4);
+ g();
+ case 5:
+ f(5);
+ g();
+ case 6:
+ f(6);
+ g();
+ default:
+ print(9);
+ }
+}
+function switchh2$1(sp) {
+ sp = sp | 0;
+ var helper$0 = 0, helper$1 = 0;
+ helper$0 = HEAP32[sp + 8 >> 2] | 0;
+ helper$1 = HEAP32[sp + 16 >> 2] | 0;
+ if (helper$0) {
+ helper$0 = 0;
+ switch (helper$1 | 0) {
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 3:
+ f(3);
+ g();
+ break;
+ default:
+ {
+ helper$0 = 1;
+ }
+ }
+ }
+ HEAP32[sp + 8 >> 2] = helper$0;
+}
+function switchh2$2(sp) {
+ sp = sp | 0;
+ var helper$0 = 0, helper$1 = 0;
+ helper$0 = HEAP32[sp + 8 >> 2] | 0;
+ helper$1 = HEAP32[sp + 16 >> 2] | 0;
+ OL : do {
+ if (helper$0) {
+ helper$0 = 0;
+ switch (helper$1 | 0) {
+ case 1:
+ f(1);
+ g();
+ return;
+ default:
+ {
+ helper$0 = 1;
+ }
+ }
+ }
+ if (helper$0) {
+ helper$0 = 0;
+ switch (helper$1 | 0) {
+ case 2:
+ f(2);
+ g();
+ break;
+ default:
+ {
+ helper$0 = 1;
+ }
+ }
+ }
+ } while (0);
+ HEAP32[sp + 8 >> 2] = helper$0;
+}
diff --git a/tools/test-js-optimizer-asm-outline1.js b/tools/test-js-optimizer-asm-outline1.js
index 4282ec8e..b7ec9011 100644
--- a/tools/test-js-optimizer-asm-outline1.js
+++ b/tools/test-js-optimizer-asm-outline1.js
@@ -307,5 +307,45 @@ function switchh() {
}
}
}
+function switchh2() {
+ while (1) switch (x) {
+ case 0:
+ f(0);
+ g();
+ break;
+ case 1:
+ f(1);
+ g();
+ return;
+ case 2:
+ f(2);
+ g();
+ break;
+ case 21: // gotta keem em unseparated
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 3: // these too
+ f(3);
+ g();
+ break;
+ case 4:
+ f(4);
+ g();
+ case 5:
+ f(5);
+ g();
+ case 6:
+ f(6);
+ g();
+ default:
+ print(9);
+ }
+}
// EMSCRIPTEN_GENERATED_FUNCTIONS
// EXTRA_INFO: { "sizeToOutline": 30, "allowCostlyOutlines": 1 }
diff --git a/tools/test-js-optimizer-regs-output.js b/tools/test-js-optimizer-regs-output.js
index 149ca984..6f67bcec 100644
--- a/tools/test-js-optimizer-regs-output.js
+++ b/tools/test-js-optimizer-regs-output.js
@@ -175,19 +175,19 @@ function fcntl_open() {
return null;
}
function ex() {
- var r1, r2;
- r1 = STACKTOP;
+ var __stackBase__ = STACKTOP;
STACKTOP += 4;
- r2 = r1;
- r1 = _puts(STRING_TABLE._str17 | 0);
- r1 = r2 | 0;
- r2 = 0;
+ var $e1 = __stackBase__;
+ var $puts = _puts(STRING_TABLE._str17 | 0);
+ var $x41 = $e1 | 0;
+ var $i_04 = 0;
while (1) {
- r1 = _printf(STRING_TABLE.__str15 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = r2, tempInt));
+ var $i_04;
+ var $call1 = _printf(STRING_TABLE.__str15 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = $i_04, tempInt));
((function() {
try {
__THREW__ = false;
- return __Z5magici(r2);
+ return __Z5magici($i_04);
} catch (e) {
if (typeof e != "number") throw e;
if (ABORT) throw e;
diff --git a/tools/test-js-optimizer-regs.js b/tools/test-js-optimizer-regs.js
index 00303786..bc0ebb5a 100644
--- a/tools/test-js-optimizer-regs.js
+++ b/tools/test-js-optimizer-regs.js
@@ -190,7 +190,7 @@ function ex() {
while (1) {
var $i_04;
var $call1 = _printf(STRING_TABLE.__str15 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = $i_04, tempInt));
- ((function() {
+ ((function() { // prevents registerize, looks like inline asm
try {
__THREW__ = false;
return __Z5magici($i_04);
diff --git a/tools/validate_asmjs.py b/tools/validate_asmjs.py
new file mode 100644
index 00000000..ea909fbd
--- /dev/null
+++ b/tools/validate_asmjs.py
@@ -0,0 +1,82 @@
+#!/usr/bin/python
+
+# This is a helper script to validate a file for asm.js.
+
+# cmdline usage: 'python validate_asmjs.py filename.{html/js}'
+# Prints a line starting with 'OK: ' on success, and returns process exit code 0.
+# On failure, prints a line starting with 'FAIL: ', and returns a nonzero process exit code.
+
+# python usage: 'validate_asmjs("filename.{html/js}", muteOutput=True/False)'
+# Returns True/False depending on whether the file was valid asm.js.
+
+# This script depends on the SpiderMonkey JS engine, which must be present in PATH in order for this script to function.
+
+import subprocess, sys, re, tempfile, os, time
+import shared
+
+# Looks up SpiderMonkey engine using the variable SPIDERMONKEY_ENGINE in ~/.emscripten, and if not set up there, via PATH.
+def find_spidermonkey_engine():
+ sm_engine = shared.SPIDERMONKEY_ENGINE if hasattr(shared, 'SPIDERMONKEY_ENGINE') else ['']
+ if not sm_engine or len(sm_engine[0]) == 0 or not os.path.exists(sm_engine[0]):
+ sm_engine[0] = shared.Building.which('js')
+ if sm_engine[0] == None:
+ return ['js-not-found']
+ return sm_engine
+
+# Given a .js file, returns True/False depending on if that file is valid asm.js
+def validate_asmjs_jsfile(filename, muteOutput):
+ process = subprocess.Popen(find_spidermonkey_engine() + ['-c', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
+ (stdout, stderr) = process.communicate()
+ if not muteOutput:
+ if len(stdout.strip()) > 0:
+ print stdout.strip()
+ if len(stderr.strip()) > 0:
+ # Pretty-print the output not to contain a spurious warning.
+ stderr = stderr.replace('warning: successfully compiled asm.js', ' successfully compiled asm.js')
+
+ print >> sys.stderr, stderr.strip()
+ if 'successfully compiled asm.js' in stderr:
+ return True
+ else:
+ return False
+
+# This tool takes as input a file built with Emscripten (either .html or .js) and validates it for asm.js.
+# Returns True/False denoting whether the file was valid asm.js. In case of a .html file, all <script>content</script> tags are searched,
+# and the ones containing a "use asm" section are validated.
+def validate_asmjs(filename, muteOutput):
+ if filename.endswith('.html'):
+ html = open(filename, 'r').read()
+ matches = re.findall('''<\w*script\w*.*?>(.*?)<\w*/script\w*>''', html, re.DOTALL | re.MULTILINE)
+ numAsmJsBlocks = 0
+ for match in matches:
+ if '"use asm"' in match:
+ numAsmJsBlocks = numAsmJsBlocks + 1
+ tmp_js = tempfile.mkstemp(suffix='.js')
+ os.write(tmp_js[0], match)
+ os.close(tmp_js[0])
+ valid_asmjs = validate_asmjs_jsfile(tmp_js[1], muteOutput)
+ os.remove(tmp_js[1])
+ if not valid_asmjs:
+ return False
+ if numAsmJsBlocks == 0:
+ if not muteOutput:
+ print >> sys.stderr, 'Error: the file does not contain any "use asm" modules.'
+ return False
+ else:
+ return True
+ else:
+ return validate_asmjs_jsfile(filename, muteOutput)
+
+def main():
+ if len(sys.argv) < 2:
+ print 'Usage: validate_asmjs <filename>'
+ return 2
+ if validate_asmjs(sys.argv[1], muteOutput=False):
+ print "OK: File '" + sys.argv[1] + "' validates as asm.js"
+ return 0
+ else:
+ print "FAIL: File '" + sys.argv[1] + "' is not valid asm.js"
+ return 1
+
+if __name__ == '__main__':
+ sys.exit(main())