diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/bisect_pair.py | 84 | ||||
-rw-r--r-- | tools/eliminator/eliminator-test-output.js | 15 | ||||
-rw-r--r-- | tools/eliminator/eliminator-test.js | 18 | ||||
-rw-r--r-- | tools/eliminator/eliminator.coffee | 12 | ||||
-rwxr-xr-x | tools/exec_llvm.py | 7 | ||||
-rw-r--r-- | tools/find_bigis.py | 18 | ||||
-rw-r--r-- | tools/nativize_llvm.py | 31 | ||||
-rw-r--r-- | tools/shared.py | 28 |
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 |