aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/bisect_pair.py84
-rw-r--r--tools/eliminator/eliminator-test-output.js15
-rw-r--r--tools/eliminator/eliminator-test.js18
-rw-r--r--tools/eliminator/eliminator.coffee12
-rwxr-xr-xtools/exec_llvm.py7
-rw-r--r--tools/find_bigis.py18
-rw-r--r--tools/nativize_llvm.py31
-rw-r--r--tools/shared.py28
8 files changed, 190 insertions, 23 deletions
diff --git a/tools/bisect_pair.py b/tools/bisect_pair.py
new file mode 100644
index 00000000..3b880b28
--- /dev/null
+++ b/tools/bisect_pair.py
@@ -0,0 +1,84 @@
+'''
+Given two similar files, for example one with an additional optimization pass,
+and with different results, will bisect between them to find the smallest
+diff that makes the outputs different.
+'''
+
+import os, sys, shutil
+from subprocess import Popen, PIPE, STDOUT
+
+__rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
+def path_from_root(*pathelems):
+ return os.path.join(__rootpath__, *pathelems)
+exec(open(path_from_root('tools', 'shared.py'), 'r').read())
+
+file1 = open(sys.argv[1]).read()
+file2 = open(sys.argv[2]).read()
+
+leftf = open('left', 'w')
+leftf.write(file1)
+leftf.close()
+
+rightf = open('right', 'w')
+rightf.write(file2)
+rightf.close()
+
+print 'running files'
+left_result = run_js('left', stderr=PIPE)
+right_result = run_js('right', stderr=PIPE) # right as in left-right, not as in correct
+assert left_result != right_result
+
+# Calculate diff chunks
+print 'diffing'
+diff = Popen(['diff', '-U', '5', 'left', 'right'], stdout=PIPE).communicate()[0].split('\n')
+pre_diff = diff[:2]
+diff = diff[2:]
+
+chunks = []
+curr = []
+for i in range(len(diff)):
+ if diff[i].startswith('@'):
+ if len(curr) > 0:
+ chunks.append(curr)
+ curr = [diff[i]]
+ else:
+ curr.append(diff[i])
+if len(curr) > 0:
+ chunks.append(curr)
+
+# Bisect both sides of the span, until we have a single chunk
+low = 0
+high = len(chunks)
+
+print 'beginning bisection, %d chunks' % high
+
+while high-low > 2:
+ mid = (low + high)/2
+ print ' current status: %d - %d - %d' % (low, mid, high)
+ # Take chunks from the middle and on. This is important because the eliminator removes variables, so starting from the beginning will add errors
+ curr_diff = '\n'.join(map(lambda parts: '\n'.join(parts), chunks[mid:])) + '\n'
+ difff = open('diff.diff', 'w')
+ difff.write(curr_diff)
+ difff.close()
+ shutil.copy('left', 'middle')
+ Popen(['patch', 'middle', 'diff.diff'], stdout=PIPE, stderr=PIPE).communicate()
+ result = run_js('middle', stderr=PIPE)
+ if result == left_result:
+ high = mid+1
+ else:
+ low = mid
+
+critical = '\n'.join(chunks[low]) + '\n'
+
+c = open('critical.diff', 'w')
+c.write(critical)
+c.close()
+print 'sanity check'
+shutil.copy('middle', 'middle2')
+Popen(['patch', 'middle2', 'critical.diff'], stdout=PIPE, stderr=PIPE).communicate()
+assert run_js('middle', stderr=PIPE) == left_result
+assert run_js('middle2', stderr=PIPE) != left_result
+
+print 'middle is like left, middle2 is like right, critical.diff is the difference that matters,'
+print critical
+
diff --git a/tools/eliminator/eliminator-test-output.js b/tools/eliminator/eliminator-test-output.js
index 594508d0..876340de 100644
--- a/tools/eliminator/eliminator-test-output.js
+++ b/tools/eliminator/eliminator-test-output.js
@@ -105,4 +105,17 @@ function t() {
__label__ = 4;
}
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t"]
+function f2() {
+ var $_pre = HEAPU32[($vla + ($storemerge312 << 2) | 0) >> 2];
+ var $storemerge312 = $storemerge312 + 1 | 0;
+ var $8 = $_pre;
+ c($8);
+}
+function f3($s, $tree, $k) {
+ var $0 = HEAPU32[($s + 2908 + ($k << 2) | 0) >> 2];
+ while (1) {
+ HEAP32[($s + 2908 + ($storemerge_in << 2) | 0) >> 2] = $9;
+ }
+ HEAP32[($s + 2908 + ($storemerge_in << 2) | 0) >> 2] = $0;
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t", "f2", "f3"]
diff --git a/tools/eliminator/eliminator-test.js b/tools/eliminator/eliminator-test.js
index 02ac0c08..4928134a 100644
--- a/tools/eliminator/eliminator-test.js
+++ b/tools/eliminator/eliminator-test.js
@@ -114,5 +114,21 @@ function t() {
var $cmp3=($12) < ($13);
if (!($cmp3)) { __label__ = 4; }
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t"]
+function f2() {
+ var $arrayidx64_phi_trans_insert = $vla + ($storemerge312 << 2) | 0;
+ var $_pre = HEAPU32[$arrayidx64_phi_trans_insert >> 2];
+ var $phitmp = $storemerge312 + 1 | 0;
+ var $storemerge312 = $phitmp;
+ var $8 = $_pre;
+ c($8);
+}
+function f3($s, $tree, $k) {
+ // HEAP vars alias each other, and the loop can confuse us
+ var $0 = HEAPU32[($s + 2908 + ($k << 2) | 0) >> 2];
+ while (1) {
+ HEAP32[($s + 2908 + ($storemerge_in << 2) | 0) >> 2] = $9;
+ }
+ HEAP32[($s + 2908 + ($storemerge_in << 2) | 0) >> 2] = $0;
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t", "f2", "f3"]
diff --git a/tools/eliminator/eliminator.coffee b/tools/eliminator/eliminator.coffee
index 8b99338a..5b8ac43f 100644
--- a/tools/eliminator/eliminator.coffee
+++ b/tools/eliminator/eliminator.coffee
@@ -252,18 +252,22 @@ class Eliminator
traverseChild child
else
# Don't put anything from outside into the body of a loop.
- savedLive = isLive
isLive = {}
for child in node then traverseChild child
- for name of isLive
- if not isLive[name] then savedLive[name] = false
- isLive = savedLive
+ # Don't keep anything alive through a loop
+ isLive = {}
return node
else if type is 'var'
for [varName, varValue] in node[1]
if varValue? then traverse varValue, checkForMutations
+ # Mark the variable as live
if @isSingleDef[varName]
isLive[varName] = true
+ # Mark variables that depend on it as no longer live
+ if @dependsOn[varName]?
+ for varNameDep of @dependsOn[varName]
+ if isLive[varNameDep]
+ isLive[varNameDep] = false
return node
else
checkForMutations node, type
diff --git a/tools/exec_llvm.py b/tools/exec_llvm.py
index a2a000f0..71c0f18d 100755
--- a/tools/exec_llvm.py
+++ b/tools/exec_llvm.py
@@ -26,11 +26,8 @@ it runs
python $(EMSCRIPTEN_TOOLS)/exec_llvm.py THE_FILE PARAMS
An alternative solution to this problem is to compile
-the .ll into native code. This can be done as follows:
-
- * Use llc to generate x86 asm
- * Use as to generate an object file
- * Use g++ to link it to an executable
+the .ll into native code, see nativize_llvm.py. That is
+useful when this fails.
'''
import os, sys
diff --git a/tools/find_bigis.py b/tools/find_bigis.py
new file mode 100644
index 00000000..d11c1a81
--- /dev/null
+++ b/tools/find_bigis.py
@@ -0,0 +1,18 @@
+'''
+Simple tool to find big i types in an .ll file. Anything over i64 is of interest.
+'''
+
+import os, sys, re
+
+filename = sys.argv[1]
+data = open(filename).read()
+iss = re.findall('[^%]i\d+ [^=]', data)
+set_iss = set(iss)
+bigs = []
+for iss in set_iss:
+ size = int(iss[2:-2])
+ if size > 64:
+ bigs.append(size)
+bigs.sort()
+print bigs
+
diff --git a/tools/nativize_llvm.py b/tools/nativize_llvm.py
new file mode 100644
index 00000000..de78dce2
--- /dev/null
+++ b/tools/nativize_llvm.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+
+'''
+Small utility to build some llvm bitcode into native code. Useful when lli (called
+from exec_llvm) fails for some reason.
+
+ * Use llc to generate x86 asm
+ * Use as to generate an object file
+ * Use g++ to link it to an executable
+'''
+
+import os, sys
+from subprocess import Popen, PIPE, STDOUT
+
+__rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
+def path_from_root(*pathelems):
+ return os.path.join(__rootpath__, *pathelems)
+exec(open(path_from_root('tools', 'shared.py'), 'r').read())
+
+filename = sys.argv[1]
+libs = sys.argv[2:] # e.g.: dl for dlopen/dlclose, util for openpty/forkpty
+
+print 'bc => clean bc'
+Popen([LLVM_OPT, filename, '-strip-debug', '-o=' + filename + '.clean.bc']).communicate()[0]
+print 'bc => s'
+Popen([LLVM_COMPILER, filename + '.clean.bc', '-o=' + filename + '.s']).communicate()[0]
+print 's => o'
+Popen(['as', filename + '.s', '-o', filename + '.o']).communicate()[0]
+print 'o => runnable'
+Popen(['g++', path_from_root('system', 'lib', 'debugging.cpp'), filename + '.o', '-o', filename + '.run'] + ['-l' + lib for lib in libs]).communicate()[0]
+
diff --git a/tools/shared.py b/tools/shared.py
index 3e989844..1f184de7 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -412,10 +412,10 @@ class Building:
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)
- Building.configure(configure + configure_args, stdout=open(os.path.join(output_dir, 'configure_'), 'w'),
- stderr=open(os.path.join(output_dir, 'configure_err'), 'w'), env=env)
- Building.make(make + make_args, stdout=open(os.path.join(output_dir, 'make_'), 'w'),
- stderr=open(os.path.join(output_dir, 'make_err'), 'w'), env=env)
+ Building.configure(configure + configure_args, stdout=open(os.path.join(project_dir, 'configure_'), 'w'),
+ stderr=open(os.path.join(project_dir, 'configure_err'), 'w'), env=env)
+ Building.make(make + make_args, stdout=open(os.path.join(project_dir, 'make_'), 'w'),
+ stderr=open(os.path.join(project_dir, 'make_err'), 'w'), env=env)
bc_file = os.path.join(project_dir, 'bc.bc')
Building.link(generated_libs, bc_file)
if cache is not None:
@@ -443,9 +443,9 @@ class Building:
# @param opt Either an integer, in which case it is the optimization level (-O1, -O2, etc.), or a list of raw
# optimization passes passed to llvm opt
@staticmethod
- def llvm_opt(filename, opts, safe=True):
+ def llvm_opt(filename, opts):
if type(opts) is int:
- opts = Building.pick_llvm_opts(opts, safe)
+ opts = Building.pick_llvm_opts(opts)
output = Popen([LLVM_OPT, filename] + opts + ['-o=' + filename + '.opt.bc'], stdout=PIPE).communicate()[0]
assert os.path.exists(filename + '.opt.bc'), 'Failed to run llvm optimizations: ' + output
shutil.move(filename + '.opt.bc', filename)
@@ -531,7 +531,7 @@ class Building:
return filename + '.o.js'
@staticmethod
- def pick_llvm_opts(optimization_level, safe=True):
+ def pick_llvm_opts(optimization_level):
'''
It may be safe to use nonportable optimizations (like -OX) if we remove the platform info from the .ll
(which we do in do_ll_opts) - but even there we have issues (even in TA2) with instruction combining
@@ -542,14 +542,18 @@ class Building:
llvm-as < /dev/null | opt -std-compile-opts -disable-output -debug-pass=Arguments
'''
+ assert 0 <= optimization_level <= 3
+ safe = Settings.USE_TYPED_ARRAYS != 2
opts = []
if optimization_level > 0:
- #opts.append('-disable-inlining') # we prefer to let closure compiler do our inlining
if not safe:
- #opts.append('-O%d' % optimization_level)
- opts.append('-std-compile-opts')
- opts.append('-std-link-opts')
- print 'Unsafe:', opts,
+ opts.append('-disable-inlining') # we prefer to let closure compiler do our inlining, to avoid overly aggressive inlining
+ # -Ox opts do -globaldce, which removes stuff that is needed for libraries and linkables
+ if not Settings.BUILD_AS_SHARED_LIB and not Settings.LINKABLE:
+ opts.append('-O%d' % optimization_level)
+ else:
+ opts.append('-std-compile-opts')
+ print '[unsafe: %s]' % ','.join(opts)
else:
allow_nonportable = not safe
optimize_size = True