webruby 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/webruby/rake/mruby.rake +1 -0
- data/modules/emscripten/AUTHORS +7 -0
- data/modules/emscripten/LICENSE +26 -0
- data/modules/emscripten/emcc +58 -20
- data/modules/emscripten/emlink.py +2 -265
- data/modules/emscripten/emscripten.py +38 -18
- data/modules/emscripten/scons-tools/emscripten.py +11 -6
- data/modules/emscripten/scons-tools/llvm.py +4 -3
- data/modules/emscripten/src/analyzer.js +32 -13
- data/modules/emscripten/src/embind/embind.js +13 -13
- data/modules/emscripten/src/embind/emval.js +8 -7
- data/modules/emscripten/src/intertyper.js +10 -0
- data/modules/emscripten/src/jsifier.js +28 -6
- data/modules/emscripten/src/library.js +2949 -1322
- data/modules/emscripten/src/library_browser.js +27 -23
- data/modules/emscripten/src/library_egl.js +7 -2
- data/modules/emscripten/src/library_gl.js +15 -2
- data/modules/emscripten/src/library_glut.js +1 -1
- data/modules/emscripten/src/library_jansson.js +1 -1
- data/modules/emscripten/src/library_openal.js +464 -132
- data/modules/emscripten/src/library_path.js +134 -0
- data/modules/emscripten/src/library_sdl.js +222 -50
- data/modules/emscripten/src/modules.js +22 -5
- data/modules/emscripten/src/parseTools.js +13 -2
- data/modules/emscripten/src/postamble.js +60 -34
- data/modules/emscripten/src/preamble.js +67 -18
- data/modules/emscripten/src/relooper/Relooper.cpp +1 -1
- data/modules/emscripten/src/runtime.js +12 -2
- data/modules/emscripten/src/settings.js +869 -826
- data/modules/emscripten/src/shell.js +63 -51
- data/modules/emscripten/src/utility.js +6 -0
- data/modules/emscripten/system/include/bsd/sys/mman.h +1 -1
- data/modules/emscripten/system/include/emscripten/bind.h +28 -28
- data/modules/emscripten/system/include/libc/math.h +8 -0
- data/modules/emscripten/system/include/libc/sys/signal.h +3 -1
- data/modules/emscripten/system/include/libc/sys/stat.h +2 -1
- data/modules/emscripten/system/include/libc/sys/types.h +4 -0
- data/modules/emscripten/system/include/libcxx/CREDITS.TXT +24 -0
- data/modules/emscripten/system/include/libcxx/__bit_reference +27 -8
- data/modules/emscripten/system/include/libcxx/__config +62 -15
- data/modules/emscripten/system/include/libcxx/__debug +5 -1
- data/modules/emscripten/system/include/libcxx/__functional_03 +24 -24
- data/modules/emscripten/system/include/libcxx/__functional_base +22 -4
- data/modules/emscripten/system/include/libcxx/__hash_table +566 -54
- data/modules/emscripten/system/include/libcxx/__locale +11 -3
- data/modules/emscripten/system/include/libcxx/__split_buffer +6 -6
- data/modules/emscripten/system/include/libcxx/__std_stream +58 -30
- data/modules/emscripten/system/include/libcxx/__tree +58 -51
- data/modules/emscripten/system/include/libcxx/__tuple +9 -9
- data/modules/emscripten/system/include/libcxx/algorithm +223 -13
- data/modules/emscripten/system/include/libcxx/array +18 -17
- data/modules/emscripten/system/include/libcxx/atomic +15 -5
- data/modules/emscripten/system/include/libcxx/cctype +2 -2
- data/modules/emscripten/system/include/libcxx/chrono +131 -36
- data/modules/emscripten/system/include/libcxx/cmath +41 -36
- data/modules/emscripten/system/include/libcxx/complex +49 -49
- data/modules/emscripten/system/include/libcxx/cstdio +2 -2
- data/modules/emscripten/system/include/libcxx/cstdlib +5 -5
- data/modules/emscripten/system/include/libcxx/cstring +2 -2
- data/modules/emscripten/system/include/libcxx/cwchar +22 -13
- data/modules/emscripten/system/include/libcxx/deque +27 -14
- data/modules/emscripten/system/include/libcxx/forward_list +36 -35
- data/modules/emscripten/system/include/libcxx/fstream +16 -0
- data/modules/emscripten/system/include/libcxx/functional +348 -23
- data/modules/emscripten/system/include/libcxx/future +66 -0
- data/modules/emscripten/system/include/libcxx/ios +27 -0
- data/modules/emscripten/system/include/libcxx/istream +2 -4
- data/modules/emscripten/system/include/libcxx/iterator +17 -9
- data/modules/emscripten/system/include/libcxx/limits +2 -2
- data/modules/emscripten/system/include/libcxx/list +165 -105
- data/modules/emscripten/system/include/libcxx/locale +154 -43
- data/modules/emscripten/system/include/libcxx/map +165 -224
- data/modules/emscripten/system/include/libcxx/memory +113 -54
- data/modules/emscripten/system/include/libcxx/random +2 -29
- data/modules/emscripten/system/include/libcxx/readme.txt +1 -1
- data/modules/emscripten/system/include/libcxx/regex +60 -15
- data/modules/emscripten/system/include/libcxx/sstream +124 -40
- data/modules/emscripten/system/include/libcxx/string +345 -182
- data/modules/emscripten/system/include/libcxx/support/win32/limits_win32.h +3 -3
- data/modules/emscripten/system/include/libcxx/support/win32/locale_win32.h +15 -2
- data/modules/emscripten/system/include/libcxx/support/win32/math_win32.h +3 -3
- data/modules/emscripten/system/include/libcxx/support/win32/support.h +10 -11
- data/modules/emscripten/system/include/libcxx/thread +2 -2
- data/modules/emscripten/system/include/libcxx/tuple +134 -65
- data/modules/emscripten/system/include/libcxx/type_traits +232 -24
- data/modules/emscripten/system/include/libcxx/unordered_map +314 -161
- data/modules/emscripten/system/include/libcxx/unordered_set +160 -2
- data/modules/emscripten/system/include/libcxx/utility +225 -40
- data/modules/emscripten/system/include/libcxx/vector +52 -57
- data/modules/emscripten/system/include/net/if.h +20 -1
- data/modules/emscripten/system/include/net/netinet/in.h +69 -5
- data/modules/emscripten/system/include/netdb.h +36 -0
- data/modules/emscripten/system/include/sys/ioctl.h +55 -3
- data/modules/emscripten/system/include/sys/select.h +2 -0
- data/modules/emscripten/system/include/sys/sendfile.h +16 -0
- data/modules/emscripten/system/include/sys/socket.h +181 -35
- data/modules/emscripten/system/lib/dlmalloc.c +10 -12
- data/modules/emscripten/system/lib/libc/musl/src/stdlib/ecvt.c +19 -0
- data/modules/emscripten/system/lib/libc/musl/src/stdlib/fcvt.c +25 -0
- data/modules/emscripten/system/lib/libc/musl/src/stdlib/gcvt.c +8 -0
- data/modules/emscripten/system/lib/libcextra.symbols +3 -0
- data/modules/emscripten/system/lib/libcxx/CREDITS.TXT +24 -0
- data/modules/emscripten/system/lib/libcxx/debug.cpp +11 -9
- data/modules/emscripten/system/lib/libcxx/exception.cpp +9 -0
- data/modules/emscripten/system/lib/libcxx/hash.cpp +6 -0
- data/modules/emscripten/system/lib/libcxx/iostream.cpp +4 -4
- data/modules/emscripten/system/lib/libcxx/locale.cpp +91 -42
- data/modules/emscripten/system/lib/libcxx/readme.txt +1 -1
- data/modules/emscripten/system/lib/libcxx/stdexcept.cpp +1 -1
- data/modules/emscripten/system/lib/libcxx/string.cpp +332 -491
- data/modules/emscripten/system/lib/libcxx/support/win32/locale_win32.cpp +4 -2
- data/modules/emscripten/system/lib/libcxx/support/win32/support.cpp +140 -41
- data/modules/emscripten/system/lib/libcxx/symbols +9 -256
- data/modules/emscripten/system/lib/libcxx/system_error.cpp +3 -0
- data/modules/emscripten/system/lib/libcxx/thread.cpp +16 -3
- data/modules/emscripten/system/lib/libcxx/typeinfo.cpp +12 -2
- data/modules/emscripten/third_party/stb_image.c +4673 -0
- data/modules/emscripten/tools/asm_module.py +273 -0
- data/modules/emscripten/tools/exec_llvm.py +2 -2
- data/modules/emscripten/tools/file_packager.py +36 -16
- data/modules/emscripten/tools/find_bigfuncs.py +9 -9
- data/modules/emscripten/tools/js-optimizer.js +485 -131
- data/modules/emscripten/tools/js_optimizer.py +22 -15
- data/modules/emscripten/tools/merge_asm.py +26 -0
- data/modules/emscripten/tools/nativize_llvm.py +2 -2
- data/modules/emscripten/tools/settings_template_readonly.py +1 -1
- data/modules/emscripten/tools/shared.py +63 -20
- data/modules/emscripten/tools/split_asm.py +30 -0
- data/modules/emscripten/tools/test-js-optimizer-asm-outline1-output.js +686 -0
- data/modules/emscripten/tools/test-js-optimizer-asm-outline1.js +263 -0
- data/modules/emscripten/tools/test-js-optimizer-asm-outline2-output.js +747 -0
- data/modules/emscripten/tools/{test-js-optimizer-asm-outline.js → test-js-optimizer-asm-outline2.js} +1 -1
- data/modules/emscripten/tools/test-js-optimizer-asm-outline3-output.js +28 -0
- data/modules/emscripten/tools/test-js-optimizer-asm-outline3.js +30 -0
- data/modules/emscripten/tools/test-js-optimizer-asm-pre-output.js +4 -4
- data/modules/mruby/AUTHORS +1 -0
- data/modules/mruby/README.md +4 -2
- data/modules/mruby/build_config.rb +6 -6
- data/modules/mruby/doc/mrbgems/README.md +4 -4
- data/modules/mruby/examples/mrbgems/c_and_ruby_extension_example/mrbgem.rake +1 -1
- data/modules/mruby/examples/mrbgems/c_extension_example/mrbgem.rake +1 -1
- data/modules/mruby/examples/mrbgems/ruby_extension_example/mrbgem.rake +1 -1
- data/modules/mruby/include/mrbconf.h +3 -0
- data/modules/mruby/include/mruby/array.h +2 -2
- data/modules/mruby/include/mruby/class.h +4 -4
- data/modules/mruby/include/mruby/compile.h +1 -0
- data/modules/mruby/include/mruby/data.h +1 -1
- data/modules/mruby/include/mruby/hash.h +2 -2
- data/modules/mruby/include/mruby/irep.h +3 -2
- data/modules/mruby/include/mruby/proc.h +1 -1
- data/modules/mruby/include/mruby/range.h +1 -1
- data/modules/mruby/include/mruby/string.h +2 -2
- data/modules/mruby/include/mruby/value.h +43 -26
- data/modules/mruby/include/mruby.h +10 -2
- data/modules/mruby/minirake +2 -2
- data/modules/mruby/mrbgems/mruby-array-ext/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-bin-mirb/mrbgem.rake +4 -1
- data/modules/mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c +32 -0
- data/modules/mruby/mrbgems/mruby-bin-mruby/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c +13 -5
- data/modules/mruby/mrbgems/mruby-enum-ext/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-enum-ext/test/enum.rb +10 -11
- data/modules/mruby/mrbgems/mruby-eval/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-exit/mrbgem.rake +4 -0
- data/modules/mruby/mrbgems/mruby-exit/src/mruby-exit.c +24 -0
- data/modules/mruby/mrbgems/mruby-fiber/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-fiber/src/fiber.c +4 -2
- data/modules/mruby/mrbgems/mruby-hash-ext/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-hash-ext/test/hash.rb +5 -7
- data/modules/mruby/mrbgems/mruby-math/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-numeric-ext/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-numeric-ext/test/numeric.rb +2 -2
- data/modules/mruby/mrbgems/mruby-object-ext/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-object-ext/src/object.c +3 -3
- data/modules/mruby/mrbgems/mruby-object-ext/test/nil.rb +3 -3
- data/modules/mruby/mrbgems/mruby-object-ext/test/object.rb +1 -1
- data/modules/mruby/mrbgems/mruby-objectspace/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-objectspace/test/objectspace.rb +36 -37
- data/modules/mruby/mrbgems/mruby-print/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-proc-ext/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-proc-ext/test/proc.rb +8 -8
- data/modules/mruby/mrbgems/mruby-random/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-range-ext/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-range-ext/test/range.rb +6 -6
- data/modules/mruby/mrbgems/mruby-sprintf/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-string-ext/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-string-ext/test/string.rb +6 -6
- data/modules/mruby/mrbgems/mruby-struct/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-symbol-ext/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-symbol-ext/test/symbol.rb +2 -2
- data/modules/mruby/mrbgems/mruby-time/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-time/src/time.c +2 -8
- data/modules/mruby/mrbgems/mruby-toplevel-ext/mrbgem.rake +1 -1
- data/modules/mruby/mrbgems/mruby-toplevel-ext/test/toplevel.rb +10 -10
- data/modules/mruby/mrblib/class.rb +15 -9
- data/modules/mruby/mrblib/string.rb +12 -0
- data/modules/mruby/src/array.c +4 -3
- data/modules/mruby/src/class.c +13 -8
- data/modules/mruby/src/codegen.c +9 -8
- data/modules/mruby/src/error.c +7 -5
- data/modules/mruby/src/error.h +1 -0
- data/modules/mruby/src/etc.c +1 -1
- data/modules/mruby/src/gc.c +163 -128
- data/modules/mruby/src/kernel.c +43 -15
- data/modules/mruby/src/numeric.c +9 -7
- data/modules/mruby/src/object.c +1 -1
- data/modules/mruby/src/parse.y +37 -19
- data/modules/mruby/src/range.c +10 -24
- data/modules/mruby/src/state.c +2 -6
- data/modules/mruby/src/string.c +0 -9
- data/modules/mruby/src/variable.c +2 -2
- data/modules/mruby/src/vm.c +12 -6
- data/modules/mruby/tasks/mrbgem_spec.rake +7 -0
- data/modules/mruby/tasks/mrbgems.rake +2 -1
- data/modules/mruby/tasks/mrbgems_test.rake +1 -1
- data/modules/mruby/tasks/mruby_build.rake +4 -3
- data/modules/mruby/tasks/mruby_build_commands.rake +6 -1
- data/modules/mruby/tasks/mruby_build_gem.rake +2 -2
- data/modules/mruby/tasks/toolchains/androideabi.rake +2 -0
- data/modules/mruby/test/assert.rb +2 -2
- data/modules/mruby/test/t/argumenterror.rb +3 -3
- data/modules/mruby/test/t/array.rb +55 -55
- data/modules/mruby/test/t/basicobject.rb +1 -1
- data/modules/mruby/test/t/bs_block.rb +12 -12
- data/modules/mruby/test/t/class.rb +21 -21
- data/modules/mruby/test/t/enumerable.rb +18 -18
- data/modules/mruby/test/t/exception.rb +20 -20
- data/modules/mruby/test/t/false.rb +3 -3
- data/modules/mruby/test/t/float.rb +40 -40
- data/modules/mruby/test/t/gc.rb +10 -10
- data/modules/mruby/test/t/hash.rb +41 -41
- data/modules/mruby/test/t/indexerror.rb +2 -2
- data/modules/mruby/test/t/integer.rb +41 -41
- data/modules/mruby/test/t/kernel.rb +33 -33
- data/modules/mruby/test/t/literals.rb +82 -82
- data/modules/mruby/test/t/localjumperror.rb +1 -1
- data/modules/mruby/test/t/module.rb +170 -31
- data/modules/mruby/test/t/nameerror.rb +5 -5
- data/modules/mruby/test/t/nil.rb +2 -2
- data/modules/mruby/test/t/nomethoderror.rb +1 -1
- data/modules/mruby/test/t/numeric.rb +5 -5
- data/modules/mruby/test/t/object.rb +2 -2
- data/modules/mruby/test/t/proc.rb +8 -8
- data/modules/mruby/test/t/range.rb +9 -9
- data/modules/mruby/test/t/rangeerror.rb +2 -2
- data/modules/mruby/test/t/runtimeerror.rb +1 -1
- data/modules/mruby/test/t/standarderror.rb +2 -2
- data/modules/mruby/test/t/string.rb +100 -100
- data/modules/mruby/test/t/symbol.rb +5 -5
- data/modules/mruby/test/t/syntax.rb +15 -6
- data/modules/mruby/test/t/true.rb +3 -3
- data/modules/mruby/test/t/typeerror.rb +2 -2
- data/modules/mruby/tools/mrbc/mrbc.c +10 -4
- data/modules/mruby/travis_config.rb +1 -0
- data/scripts/gen_gems_config.rb +5 -1
- metadata +19 -4
- data/modules/emscripten/tools/test-js-optimizer-asm-outline-output.js +0 -570
@@ -410,7 +410,7 @@ function removeUnneededLabelSettings(ast) {
|
|
410
410
|
var USEFUL_BINARY_OPS = set('<<', '>>', '|', '&', '^');
|
411
411
|
var COMPARE_OPS = set('<', '<=', '>', '>=', '==', '===', '!=', '!==');
|
412
412
|
|
413
|
-
function
|
413
|
+
function simplifyExpressions(ast) {
|
414
414
|
// Simplify common expressions used to perform integer conversion operations
|
415
415
|
// in cases where no conversion is needed.
|
416
416
|
function simplifyIntegerConversions(ast) {
|
@@ -793,6 +793,7 @@ function simplifyExpressionsPre(ast) {
|
|
793
793
|
joinAdditions(func);
|
794
794
|
// simplifyZeroComp(func); TODO: investigate performance
|
795
795
|
if (asm) asmOpts(func);
|
796
|
+
simplifyNotComps(func);
|
796
797
|
});
|
797
798
|
}
|
798
799
|
|
@@ -1158,10 +1159,6 @@ function simplifyNotComps(ast) {
|
|
1158
1159
|
simplifyNotCompsPass = false;
|
1159
1160
|
}
|
1160
1161
|
|
1161
|
-
function simplifyExpressionsPost(ast) {
|
1162
|
-
simplifyNotComps(ast);
|
1163
|
-
}
|
1164
|
-
|
1165
1162
|
var NO_SIDE_EFFECTS = set('num', 'name');
|
1166
1163
|
|
1167
1164
|
function hasSideEffects(node) { // this is 99% incomplete!
|
@@ -1540,24 +1537,22 @@ function detectAsmCoercion(node, asmInfo) {
|
|
1540
1537
|
// for params, +x vs x|0, for vars, 0.0 vs 0
|
1541
1538
|
if (node[0] === 'num' && node[1].toString().indexOf('.') >= 0) return ASM_DOUBLE;
|
1542
1539
|
if (node[0] === 'unary-prefix') return ASM_DOUBLE;
|
1543
|
-
if (asmInfo && node[0] == 'name')
|
1544
|
-
if (node[1] in asmInfo.vars) return asmInfo.vars[node[1]];
|
1545
|
-
if (node[1] in asmInfo.params) return asmInfo.params[node[1]];
|
1546
|
-
}
|
1540
|
+
if (asmInfo && node[0] == 'name') return getAsmType(node[1], asmInfo);
|
1547
1541
|
return ASM_INT;
|
1548
1542
|
}
|
1549
1543
|
|
1550
|
-
function
|
1551
|
-
return type === ASM_INT ? ['binary', '|',
|
1544
|
+
function makeAsmCoercion(node, type) {
|
1545
|
+
return type === ASM_INT ? ['binary', '|', node, ['num', 0]] : ['unary-prefix', '+', node];
|
1552
1546
|
}
|
1553
1547
|
|
1554
1548
|
function makeAsmVarDef(v, type) {
|
1555
1549
|
return [v, type === ASM_INT ? ['num', 0] : ['unary-prefix', '+', ['num', 0]]];
|
1556
1550
|
}
|
1557
1551
|
|
1558
|
-
function getAsmType(
|
1552
|
+
function getAsmType(name, asmInfo) {
|
1559
1553
|
if (name in asmInfo.vars) return asmInfo.vars[name];
|
1560
|
-
return asmInfo.params[name];
|
1554
|
+
if (name in asmInfo.params) return asmInfo.params[name];
|
1555
|
+
assert(false, 'unknown var ' + name);
|
1561
1556
|
}
|
1562
1557
|
|
1563
1558
|
function normalizeAsm(func) {
|
@@ -1658,7 +1653,7 @@ function denormalizeAsm(func, data) {
|
|
1658
1653
|
// add param coercions
|
1659
1654
|
var next = 0;
|
1660
1655
|
func[2].forEach(function(param) {
|
1661
|
-
stats[next++] = ['stat', ['assign', true, ['name', param],
|
1656
|
+
stats[next++] = ['stat', ['assign', true, ['name', param], makeAsmCoercion(['name', param], data.params[param])]];
|
1662
1657
|
});
|
1663
1658
|
// add variable definitions
|
1664
1659
|
var varDefs = [];
|
@@ -1673,6 +1668,37 @@ function denormalizeAsm(func, data) {
|
|
1673
1668
|
//printErr('denormalized \n\n' + astToSrc(func) + '\n\n');
|
1674
1669
|
}
|
1675
1670
|
|
1671
|
+
function getFirstIndexInNormalized(func, data) {
|
1672
|
+
// In a normalized asm function, return the index of the first element that is not not defs or annotation
|
1673
|
+
var stats = func[3];
|
1674
|
+
var i = stats.length-1;
|
1675
|
+
while (i >= 0) {
|
1676
|
+
var stat = stats[i];
|
1677
|
+
if (stat[0] == 'var') break;
|
1678
|
+
i--;
|
1679
|
+
}
|
1680
|
+
return i+1;
|
1681
|
+
}
|
1682
|
+
|
1683
|
+
function getStackBumpNode(ast) {
|
1684
|
+
var found = null;
|
1685
|
+
traverse(ast, function(node, type) {
|
1686
|
+
if (type === 'assign' && node[2][0] === 'name' && node[2][1] === 'STACKTOP') {
|
1687
|
+
var value = node[3];
|
1688
|
+
if (value[0] === 'name') return true;
|
1689
|
+
assert(value[0] == 'binary' && value[1] == '|' && value[2][0] == 'binary' && value[2][1] == '+' && value[2][2][0] == 'name' && value[2][2][1] == 'STACKTOP' && value[2][3][0] == 'num');
|
1690
|
+
found = node;
|
1691
|
+
return true;
|
1692
|
+
}
|
1693
|
+
});
|
1694
|
+
return found;
|
1695
|
+
}
|
1696
|
+
|
1697
|
+
function getStackBumpSize(ast) {
|
1698
|
+
var node = getStackBumpNode(ast);
|
1699
|
+
return node ? node[3][2][3][1] : 0;
|
1700
|
+
}
|
1701
|
+
|
1676
1702
|
// Very simple 'registerization', coalescing of variables into a smaller number,
|
1677
1703
|
// as part of minification. Globals-level minification began in a previous pass,
|
1678
1704
|
// we receive extraInfo which tells us how to rename globals. (Only in asm.js.)
|
@@ -1717,7 +1743,7 @@ function registerize(ast) {
|
|
1717
1743
|
}
|
1718
1744
|
});
|
1719
1745
|
vacuum(fun);
|
1720
|
-
if (extraInfo) {
|
1746
|
+
if (extraInfo && extraInfo.globals) {
|
1721
1747
|
assert(asm);
|
1722
1748
|
var usedGlobals = {};
|
1723
1749
|
var nextLocal = 0;
|
@@ -1758,16 +1784,17 @@ function registerize(ast) {
|
|
1758
1784
|
}
|
1759
1785
|
}
|
1760
1786
|
});
|
1761
|
-
|
1762
|
-
|
1763
|
-
|
1787
|
+
if (fun[1] in extraInfo.globals) { // if fun was created by a previous optimization pass, it will not be here
|
1788
|
+
fun[1] = extraInfo.globals[fun[1]];
|
1789
|
+
assert(fun[1]);
|
1790
|
+
}
|
1764
1791
|
var nextRegName = 0;
|
1765
1792
|
}
|
1766
1793
|
var regTypes = {};
|
1767
1794
|
function getNewRegName(num, name) {
|
1768
1795
|
if (!asm) return 'r' + num;
|
1769
1796
|
var type = asmData.vars[name];
|
1770
|
-
if (!extraInfo) {
|
1797
|
+
if (!extraInfo || !extraInfo.globals) {
|
1771
1798
|
var ret = (type ? 'd' : 'i') + num;
|
1772
1799
|
regTypes[ret] = type;
|
1773
1800
|
return ret;
|
@@ -2790,7 +2817,7 @@ function relocate(ast) {
|
|
2790
2817
|
var other = node[3];
|
2791
2818
|
if (base === 0) return other;
|
2792
2819
|
if (other[0] == 'num') {
|
2793
|
-
other[1]
|
2820
|
+
other[1] = (other[1] + base)|0;
|
2794
2821
|
return other;
|
2795
2822
|
} else {
|
2796
2823
|
node[2] = ['num', base];
|
@@ -2846,6 +2873,7 @@ function outline(ast) {
|
|
2846
2873
|
defs[name] = node;
|
2847
2874
|
} else {
|
2848
2875
|
if (name in asmData.params) {
|
2876
|
+
assignments[name] = (assignments[name] || 1) + 1; // init to 1 for initial parameter assignment
|
2849
2877
|
considered[name] = true; // this parameter is not ssa, it must be in a hand-optimized function, so it is not trivial
|
2850
2878
|
}
|
2851
2879
|
}
|
@@ -2971,6 +2999,95 @@ function outline(ast) {
|
|
2971
2999
|
});
|
2972
3000
|
}
|
2973
3001
|
|
3002
|
+
// Try to flatten out code as much as possible, to make outlining more feasible.
|
3003
|
+
function flatten(func, asmData) {
|
3004
|
+
var minSize = sizeToOutline;
|
3005
|
+
var helperId = 0;
|
3006
|
+
function getHelper() {
|
3007
|
+
while (1) {
|
3008
|
+
var ret = 'helper$' + (helperId++);
|
3009
|
+
if (!(ret in asmData.vars) && !(ret in asmData.params)) {
|
3010
|
+
asmData.vars[ret] = ASM_INT;
|
3011
|
+
return ret;
|
3012
|
+
}
|
3013
|
+
}
|
3014
|
+
}
|
3015
|
+
var ignore = [];
|
3016
|
+
traverse(func, function(node) {
|
3017
|
+
var stats = getStatements(node);
|
3018
|
+
if (stats) {
|
3019
|
+
for (var i = 0; i < stats.length; i++) {
|
3020
|
+
var node = stats[i]; // step over param
|
3021
|
+
if (ignore.indexOf(node) >= 0) continue;
|
3022
|
+
if (node[0] == 'stat') node = node[1];
|
3023
|
+
if (ignore.indexOf(node) >= 0) continue;
|
3024
|
+
var type = node[0];
|
3025
|
+
if (measureSize(node) >= minSize) {
|
3026
|
+
if (type === 'if' && node[3]) {
|
3027
|
+
var reps = [];
|
3028
|
+
var helper = getHelper();
|
3029
|
+
// clear helper
|
3030
|
+
reps.push(['stat', ['assign', true, ['name', helper], ['num', 1]]]); // 1 means continue in ifs
|
3031
|
+
// gather parts
|
3032
|
+
var parts = [];
|
3033
|
+
var curr = node;
|
3034
|
+
while (1) {
|
3035
|
+
parts.push({ condition: curr[1], body: curr[2] });
|
3036
|
+
curr = curr[3];
|
3037
|
+
if (!curr) break;
|
3038
|
+
if (curr[0] != 'if') {
|
3039
|
+
parts.push({ condition: null, body: curr });
|
3040
|
+
break;
|
3041
|
+
}
|
3042
|
+
}
|
3043
|
+
// chunkify. Each chunk is a chain of if-elses, with the new overhead just on entry and exit
|
3044
|
+
var chunks = [];
|
3045
|
+
var currSize = 0;
|
3046
|
+
var currChunk = [];
|
3047
|
+
parts.forEach(function(part) {
|
3048
|
+
var size = (part.condition ? measureSize(part.condition) : 0) + measureSize(part.body) + 5; // add constant for overhead of extra code
|
3049
|
+
assert(size > 0);
|
3050
|
+
if (size + currSize >= minSize && currSize) {
|
3051
|
+
chunks.push(currChunk);
|
3052
|
+
currChunk = [];
|
3053
|
+
currSize = 0;
|
3054
|
+
}
|
3055
|
+
currChunk.push(part);
|
3056
|
+
currSize += size;
|
3057
|
+
});
|
3058
|
+
assert(currSize);
|
3059
|
+
chunks.push(currChunk);
|
3060
|
+
// generate flattened code
|
3061
|
+
chunks.forEach(function(chunk) {
|
3062
|
+
var pre = ['stat', ['assign', true, ['name', helper], ['num', 0]]];
|
3063
|
+
var chain = null, tail = null;
|
3064
|
+
chunk.forEach(function(part) {
|
3065
|
+
// add to chain
|
3066
|
+
var contents = makeIf(part.condition || ['num', 1], part.body[1]);
|
3067
|
+
if (chain) {
|
3068
|
+
tail[3] = contents;
|
3069
|
+
} else {
|
3070
|
+
chain = contents;
|
3071
|
+
ignore.push(contents);
|
3072
|
+
}
|
3073
|
+
tail = contents;
|
3074
|
+
});
|
3075
|
+
// if none of the ifs were entered, in the final else note that we need to continue
|
3076
|
+
tail[3] = ['block', [['stat', ['assign', true, ['name', helper], ['num', 1]]]]];
|
3077
|
+
reps.push(makeIf(['name', helper], [pre, chain]));
|
3078
|
+
});
|
3079
|
+
// replace code and update i
|
3080
|
+
stats.splice.apply(stats, [i, 1].concat(reps));
|
3081
|
+
i--; // negate loop increment
|
3082
|
+
i += reps.length;
|
3083
|
+
continue;
|
3084
|
+
}
|
3085
|
+
}
|
3086
|
+
}
|
3087
|
+
}
|
3088
|
+
});
|
3089
|
+
}
|
3090
|
+
|
2974
3091
|
// Prepares information for spilling of local variables
|
2975
3092
|
function analyzeFunction(func, asmData) {
|
2976
3093
|
var stack = []; // list of variables, each gets 8 bytes
|
@@ -2981,32 +3098,37 @@ function outline(ast) {
|
|
2981
3098
|
stack.push(name);
|
2982
3099
|
}
|
2983
3100
|
asmData.stackPos = {};
|
3101
|
+
var stackSize = getStackBumpSize(func);
|
3102
|
+
if (stackSize % 8 === 0) stackSize += 8 - (stackSize % 8);
|
2984
3103
|
for (var i = 0; i < stack.length; i++) {
|
2985
|
-
asmData.stackPos[stack[i]] = i*8;
|
2986
|
-
}
|
2987
|
-
// Reserve an extra two spots: one for control flow var, the other for control flow data
|
2988
|
-
|
2989
|
-
|
2990
|
-
asmData.
|
3104
|
+
asmData.stackPos[stack[i]] = stackSize + i*8;
|
3105
|
+
}
|
3106
|
+
// Reserve an extra two spots per possible outlining: one for control flow var, the other for control flow data
|
3107
|
+
// The control variables are zeroed out when calling an outlined function, and after using
|
3108
|
+
// the value after they return.
|
3109
|
+
asmData.maxOutlinings = Math.round(3*measureSize(func)/extraInfo.sizeToOutline);
|
3110
|
+
asmData.totalStackSize = stackSize + (stack.length + 2*asmData.maxOutlinings)*8;
|
3111
|
+
asmData.controlStackPos = function(i) { return stackSize + (stack.length + i)*8 };
|
3112
|
+
asmData.controlDataStackPos = function(i) { return stackSize + (stack.length + i)*8 + 4 };
|
2991
3113
|
asmData.splitCounter = 0;
|
2992
3114
|
}
|
2993
3115
|
|
2994
3116
|
// Analyze uses - reads and writes - of variables in part of the AST of a function
|
2995
3117
|
function analyzeCode(func, asmData, ast) {
|
2996
|
-
var labels = {};
|
3118
|
+
var labels = {}; // labels defined in this code
|
2997
3119
|
var labelCounter = 1; // 0 means no label
|
2998
3120
|
|
2999
3121
|
traverse(ast, function(node, type) {
|
3000
|
-
if (
|
3122
|
+
if (type == 'label' && !(node[1] in labels)) {
|
3001
3123
|
labels[node[1]] = labelCounter++;
|
3002
3124
|
}
|
3003
3125
|
});
|
3004
3126
|
|
3005
3127
|
var writes = {};
|
3006
|
-
var
|
3007
|
-
var hasReturn = false, hasBreak = false, hasContinue = false;
|
3128
|
+
var namings = {};
|
3129
|
+
var hasReturn = false, hasReturnInt = false, hasReturnDouble = false, hasBreak = false, hasContinue = false;
|
3008
3130
|
var breaks = {}; // set of labels we break or continue
|
3009
|
-
var continues = {}; // to
|
3131
|
+
var continues = {}; // to (name -> id, just like labels)
|
3010
3132
|
var breakCapturers = 0;
|
3011
3133
|
var continueCapturers = 0;
|
3012
3134
|
|
@@ -3014,27 +3136,32 @@ function outline(ast) {
|
|
3014
3136
|
if (type == 'assign' && node[2][0] == 'name') {
|
3015
3137
|
var name = node[2][1];
|
3016
3138
|
if (name in asmData.vars || name in asmData.params) {
|
3017
|
-
writes[name] = 0;
|
3018
|
-
appearances[name] = (appearances[name] || 0) - 1; // this appearance is a definition, offset the counting later
|
3139
|
+
writes[name] = (writes[name] || 0) + 1;
|
3019
3140
|
}
|
3020
3141
|
} else if (type == 'name') {
|
3021
3142
|
var name = node[1];
|
3022
3143
|
if (name in asmData.vars || name in asmData.params) {
|
3023
|
-
|
3144
|
+
namings[name] = (namings[name] || 0) + 1;
|
3024
3145
|
}
|
3025
3146
|
} else if (type == 'return') {
|
3026
|
-
|
3147
|
+
if (!node[1]) {
|
3148
|
+
hasReturn = true;
|
3149
|
+
} else if (detectAsmCoercion(node[1]) == ASM_INT) {
|
3150
|
+
hasReturnInt = true;
|
3151
|
+
} else {
|
3152
|
+
hasReturnDouble = true;
|
3153
|
+
}
|
3027
3154
|
} else if (type == 'break') {
|
3028
3155
|
var label = node[1] || 0;
|
3029
3156
|
if (!label && breakCapturers > 0) return; // no label, and captured
|
3030
3157
|
if (label && (label in labels)) return; // label, and defined in this code, so captured
|
3031
|
-
breaks[label
|
3158
|
+
if (label) breaks[label] = labelCounter++;
|
3032
3159
|
hasBreak = true;
|
3033
3160
|
} else if (type == 'continue') {
|
3034
3161
|
var label = node[1] || 0;
|
3035
3162
|
if (!label && continueCapturers > 0) return; // no label, and captured
|
3036
3163
|
if (label && (label in labels)) return; // label, and defined in this code, so captured
|
3037
|
-
continues[label
|
3164
|
+
if (label) continues[label] = labelCounter++;
|
3038
3165
|
hasContinue = true;
|
3039
3166
|
} else {
|
3040
3167
|
if (type in BREAK_CAPTURERS) {
|
@@ -3052,14 +3179,15 @@ function outline(ast) {
|
|
3052
3179
|
continueCapturers--;
|
3053
3180
|
}
|
3054
3181
|
});
|
3182
|
+
assert(hasReturn + hasReturnInt + hasReturnDouble <= 1);
|
3055
3183
|
|
3056
3184
|
var reads = {};
|
3057
|
-
|
3058
|
-
|
3059
|
-
if (
|
3185
|
+
for (var v in namings) {
|
3186
|
+
var actualReads = namings[v] - (writes[v] || 0);
|
3187
|
+
if (actualReads > 0) reads[v] = actualReads;
|
3060
3188
|
}
|
3061
3189
|
|
3062
|
-
return { writes: writes, reads: reads, hasReturn: hasReturn, breaks: breaks, continues: continues, labels: labels };
|
3190
|
+
return { writes: writes, reads: reads, hasReturn: hasReturn, hasReturnInt: hasReturnInt, hasReturnDouble: hasReturnDouble, hasBreak: hasBreak, hasContinue: hasContinue, breaks: breaks, continues: continues, labels: labels };
|
3063
3191
|
}
|
3064
3192
|
|
3065
3193
|
function makeAssign(dst, src) {
|
@@ -3076,76 +3204,114 @@ function outline(ast) {
|
|
3076
3204
|
function makeComparison(left, comp, right) {
|
3077
3205
|
return ['binary', comp, left, right];
|
3078
3206
|
}
|
3207
|
+
function makeSwitch(value, cases) {
|
3208
|
+
return ['switch', value, cases];
|
3209
|
+
}
|
3079
3210
|
|
3080
3211
|
var CONTROL_BREAK = 1, CONTROL_BREAK_LABEL = 2, CONTROL_CONTINUE = 3, CONTROL_CONTINUE_LABEL = 4, CONTROL_RETURN_VOID = 5, CONTROL_RETURN_INT = 6, CONTROL_RETURN_DOUBLE = 7;
|
3081
3212
|
|
3082
|
-
var sizeToOutline =
|
3213
|
+
var sizeToOutline = null; // customized per function and as we make progress
|
3214
|
+
function calculateThreshold(func) {
|
3215
|
+
sizeToOutline = extraInfo.sizeToOutline;
|
3216
|
+
var size = measureSize(func);
|
3217
|
+
//var desiredChunks = Math.ceil(size/extraInfo.sizeToOutline);
|
3218
|
+
////sizeToOutline = Math.round((extraInfo.sizeToOutline + (2*size/desiredChunks))/3);
|
3219
|
+
//sizeToOutline = Math.round(size/desiredChunks);
|
3220
|
+
printErr('trying to reduce the size of ' + func[1] + ' which is ' + size + ' (>= ' + extraInfo.sizeToOutline + '), aim for ' + sizeToOutline);
|
3221
|
+
}
|
3222
|
+
|
3083
3223
|
var level = 0;
|
3224
|
+
var outliningParents = {}; // function name => parent it was outlined from
|
3084
3225
|
|
3085
3226
|
function doOutline(func, asmData, stats, start, end) {
|
3086
|
-
|
3227
|
+
if (asmData.splitCounter === asmData.maxOutlinings) return [];
|
3228
|
+
if (!extraInfo.allowCostlyOutlines) var originalStats = copy(stats);
|
3087
3229
|
var code = stats.slice(start, end+1);
|
3088
|
-
var
|
3089
|
-
|
3230
|
+
var originalCodeSize = measureSize(code);
|
3231
|
+
var funcSize = measureSize(func);
|
3232
|
+
var outlineIndex = asmData.splitCounter++;
|
3233
|
+
var newIdent = func[1] + '$' + outlineIndex;
|
3234
|
+
// analyze variables, and find 'owned' variables - that only appear in the outlined code, and do not need any spill support
|
3090
3235
|
var codeInfo = analyzeCode(func, asmData, code);
|
3236
|
+
var allCodeInfo = analyzeCode(func, asmData, func);
|
3237
|
+
var owned = { sp: 1 }; // sp is always owned, each has its own
|
3238
|
+
keys(setUnion(codeInfo.reads, codeInfo.writes)).forEach(function(v) {
|
3239
|
+
if (allCodeInfo.reads[v] === codeInfo.reads[v] && allCodeInfo.writes[v] === codeInfo.writes[v] && !(v in asmData.params)) {
|
3240
|
+
owned[v] = 1;
|
3241
|
+
}
|
3242
|
+
});
|
3091
3243
|
var reps = [];
|
3092
|
-
|
3093
|
-
|
3094
|
-
|
3095
|
-
|
3244
|
+
// wipe out control variable
|
3245
|
+
reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', 0])]);
|
3246
|
+
reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ['num', 0])]); // XXX not really needed
|
3247
|
+
// add spills and reads before and after the call to the outlined code, and in the outlined code itself
|
3248
|
+
keys(setUnion(codeInfo.reads, codeInfo.writes)).forEach(function(v) {
|
3249
|
+
if (!(v in owned)) {
|
3250
|
+
reps.push(['stat', ['assign', true, ['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], ['name', v]]]);
|
3096
3251
|
}
|
3097
|
-
}
|
3252
|
+
});
|
3098
3253
|
reps.push(['stat', ['call', ['name', newIdent], [['name', 'sp']]]]);
|
3099
3254
|
for (var v in codeInfo.writes) {
|
3100
|
-
|
3101
|
-
|
3255
|
+
if (!(v in owned)) {
|
3256
|
+
reps.push(['stat', ['assign', true, ['name', v], makeAsmCoercion(['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], getAsmType(v, asmData))]]);
|
3257
|
+
}
|
3102
3258
|
}
|
3103
3259
|
// Generate new function
|
3104
|
-
if (codeInfo.hasReturn || codeInfo.hasBreak || codeInfo.hasContinue) {
|
3260
|
+
if (codeInfo.hasReturn || codeInfo.hasReturnInt || codeInfo.hasReturnDouble || codeInfo.hasBreak || codeInfo.hasContinue) {
|
3105
3261
|
// we need to capture all control flow using a top-level labeled one-time loop in the outlined function
|
3106
|
-
code = [['label', 'OL', ['do', ['num', 0], ['block', code]]]];
|
3107
3262
|
var breakCapturers = 0;
|
3108
3263
|
var continueCapturers = 0;
|
3109
|
-
traverse(code, function(node, type) {
|
3264
|
+
traverse(['block', code], function(node, type) { // traverse on dummy block, so we get the toplevel statements
|
3110
3265
|
// replace all break/continue/returns with code to break out of the main one-time loop, and set the control data
|
3111
|
-
if (type
|
3112
|
-
|
3113
|
-
|
3114
|
-
|
3115
|
-
|
3116
|
-
|
3117
|
-
|
3118
|
-
|
3119
|
-
|
3120
|
-
|
3121
|
-
|
3122
|
-
|
3123
|
-
|
3124
|
-
|
3125
|
-
|
3126
|
-
|
3127
|
-
|
3128
|
-
|
3129
|
-
|
3130
|
-
|
3131
|
-
|
3132
|
-
|
3133
|
-
|
3134
|
-
|
3135
|
-
|
3136
|
-
|
3137
|
-
|
3138
|
-
|
3139
|
-
|
3140
|
-
|
3141
|
-
|
3142
|
-
|
3143
|
-
|
3144
|
-
|
3145
|
-
|
3146
|
-
|
3147
|
-
|
3148
|
-
|
3266
|
+
if (type in BREAK_CAPTURERS) {
|
3267
|
+
breakCapturers++;
|
3268
|
+
}
|
3269
|
+
if (type in CONTINUE_CAPTURERS) {
|
3270
|
+
continueCapturers++;
|
3271
|
+
}
|
3272
|
+
var stats = node === code ? node : getStatements(node);
|
3273
|
+
if (stats) {
|
3274
|
+
for (var i = 0; i < stats.length; i++) {
|
3275
|
+
var node = stats[i]; // step all over node and type here, for convenience
|
3276
|
+
if (node[0] == 'stat') node = node[1];
|
3277
|
+
var type = node[0];
|
3278
|
+
var ret = null;
|
3279
|
+
if (type == 'return') {
|
3280
|
+
ret = [];
|
3281
|
+
if (!node[1]) {
|
3282
|
+
ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', CONTROL_RETURN_VOID])]);
|
3283
|
+
} else {
|
3284
|
+
var type = detectAsmCoercion(node[1], asmData);
|
3285
|
+
ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', type == ASM_INT ? CONTROL_RETURN_INT : CONTROL_RETURN_DOUBLE])]);
|
3286
|
+
ret.push(['stat', makeAssign(makeStackAccess(type, asmData.controlDataStackPos(outlineIndex)), node[1])]);
|
3287
|
+
}
|
3288
|
+
ret.push(['stat', ['break', 'OL']]);
|
3289
|
+
} else if (type == 'break') {
|
3290
|
+
var label = node[1] || 0;
|
3291
|
+
if (label == 'OL') continue; // this was just added before us, it is new replacement code
|
3292
|
+
if (!label && breakCapturers > 0) continue; // no label, and captured
|
3293
|
+
if (label && (label in codeInfo.labels)) continue; // label, and defined in this code, so captured
|
3294
|
+
ret = [['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', label ? CONTROL_BREAK_LABEL : CONTROL_BREAK])]];
|
3295
|
+
if (label) {
|
3296
|
+
assert(label in codeInfo.breaks);
|
3297
|
+
ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ['num', codeInfo.breaks[label]])]);
|
3298
|
+
}
|
3299
|
+
ret.push(['stat', ['break', 'OL']]);
|
3300
|
+
} else if (type == 'continue') {
|
3301
|
+
var label = node[1] || 0;
|
3302
|
+
if (!label && continueCapturers > 0) continue; // no label, and captured
|
3303
|
+
if (label && (label in codeInfo.labels)) continue; // label, and defined in this code, so captured
|
3304
|
+
ret = [['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', label ? CONTROL_CONTINUE_LABEL : CONTROL_CONTINUE])]];
|
3305
|
+
if (label) {
|
3306
|
+
assert(label in codeInfo.continues);
|
3307
|
+
ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ['num', codeInfo.continues[label]])]);
|
3308
|
+
}
|
3309
|
+
ret.push(['stat', ['break', 'OL']]);
|
3310
|
+
}
|
3311
|
+
if (ret) {
|
3312
|
+
stats.splice.apply(stats, [i, 1].concat(ret));
|
3313
|
+
i += ret.length-1;
|
3314
|
+
}
|
3149
3315
|
}
|
3150
3316
|
}
|
3151
3317
|
}, function(node, type) {
|
@@ -3156,70 +3322,183 @@ function outline(ast) {
|
|
3156
3322
|
continueCapturers--;
|
3157
3323
|
}
|
3158
3324
|
});
|
3159
|
-
|
3325
|
+
code = [['label', 'OL', ['do', ['num', 0], ['block', code]]]]; // do this after processing, to not confuse breakCapturers etc.
|
3326
|
+
// read the control data at the callsite to the outlined function, and clear the control values
|
3327
|
+
reps.push(['stat', makeAssign(
|
3328
|
+
['name', 'tempValue'],
|
3329
|
+
makeAsmCoercion(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ASM_INT)
|
3330
|
+
)]);
|
3331
|
+
reps.push(['stat', makeAssign(
|
3332
|
+
['name', 'tempInt'],
|
3333
|
+
makeAsmCoercion(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ASM_INT)
|
3334
|
+
)]);
|
3335
|
+
reps.push(['stat', makeAssign(
|
3336
|
+
['name', 'tempDouble'],
|
3337
|
+
makeAsmCoercion(makeStackAccess(ASM_DOUBLE, asmData.controlDataStackPos(outlineIndex)), ASM_DOUBLE)
|
3338
|
+
)]);
|
3339
|
+
reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', 0])]);
|
3340
|
+
reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ['num', 0])]); // XXX not really needed
|
3341
|
+
// use the control data information
|
3160
3342
|
if (codeInfo.hasReturn) {
|
3161
3343
|
reps.push(makeIf(
|
3162
|
-
makeComparison(
|
3344
|
+
makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_RETURN_VOID]),
|
3163
3345
|
[['stat', ['return']]]
|
3164
3346
|
));
|
3347
|
+
}
|
3348
|
+
if (codeInfo.hasReturnInt) {
|
3165
3349
|
reps.push(makeIf(
|
3166
|
-
makeComparison(
|
3167
|
-
[['stat', ['return',
|
3350
|
+
makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_RETURN_INT]),
|
3351
|
+
[['stat', ['return', makeAsmCoercion(['name', 'tempInt'], ASM_INT)]]]
|
3168
3352
|
));
|
3353
|
+
}
|
3354
|
+
if (codeInfo.hasReturnDouble) {
|
3169
3355
|
reps.push(makeIf(
|
3170
|
-
makeComparison(
|
3171
|
-
[['stat', ['return',
|
3356
|
+
makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_RETURN_DOUBLE]),
|
3357
|
+
[['stat', ['return', makeAsmCoercion(['name', 'tempDouble'], ASM_DOUBLE)]]]
|
3172
3358
|
));
|
3173
3359
|
}
|
3174
3360
|
if (codeInfo.hasBreak) {
|
3175
3361
|
reps.push(makeIf(
|
3176
|
-
makeComparison(
|
3177
|
-
['stat', ['break']]
|
3178
|
-
));
|
3179
|
-
reps.push(makeIf(
|
3180
|
-
makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_BREAK_LABEL]),
|
3181
|
-
['stat', ['break', makeStackAccess(ASM_INT, asmData.controlDataStackPos)]] // XXX here and below, need a switch overall possible labels
|
3362
|
+
makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_BREAK]),
|
3363
|
+
[['stat', ['break']]]
|
3182
3364
|
));
|
3365
|
+
if (keys(codeInfo.breaks).length > 0) {
|
3366
|
+
reps.push(makeIf(
|
3367
|
+
makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_BREAK_LABEL]),
|
3368
|
+
[makeSwitch(makeAsmCoercion(['name', 'tempInt'], ASM_INT), keys(codeInfo.breaks).map(function(key) {
|
3369
|
+
var id = codeInfo.breaks[key];
|
3370
|
+
return [['num', id], [['block', [['stat', ['break', key]]]]]];
|
3371
|
+
}))]
|
3372
|
+
));
|
3373
|
+
}
|
3183
3374
|
}
|
3184
3375
|
if (codeInfo.hasContinue) {
|
3185
3376
|
reps.push(makeIf(
|
3186
|
-
makeComparison(
|
3187
|
-
['stat', ['
|
3188
|
-
));
|
3189
|
-
reps.push(makeIf(
|
3190
|
-
makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_CONTINUE_LABEL]),
|
3191
|
-
['stat', ['continue', makeStackAccess(ASM_INT, asmData.controlDataStackPos)]] // XXX
|
3377
|
+
makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_CONTINUE]),
|
3378
|
+
[['stat', ['continue']]]
|
3192
3379
|
));
|
3380
|
+
if (keys(codeInfo.continues).length > 0) {
|
3381
|
+
reps.push(makeIf(
|
3382
|
+
makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_CONTINUE_LABEL]),
|
3383
|
+
[makeSwitch(makeAsmCoercion(['name', 'tempInt'], ASM_INT), keys(codeInfo.continues).map(function(key) {
|
3384
|
+
var id = codeInfo.continues[key];
|
3385
|
+
return [['num', id], [['block', [['stat', ['continue', key]]]]]];
|
3386
|
+
}))]
|
3387
|
+
));
|
3388
|
+
}
|
3193
3389
|
}
|
3194
3390
|
}
|
3391
|
+
// add spills and unspills in outlined code outside the OL loop
|
3392
|
+
keys(setUnion(codeInfo.reads, codeInfo.writes)).forEach(function(v) {
|
3393
|
+
if (!(v in owned)) {
|
3394
|
+
code.unshift(['stat', ['assign', true, ['name', v], makeAsmCoercion(['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], getAsmType(v, asmData))]]);
|
3395
|
+
}
|
3396
|
+
});
|
3397
|
+
for (var v in codeInfo.writes) {
|
3398
|
+
if (!(v in owned)) {
|
3399
|
+
code.push(['stat', ['assign', true, ['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], ['name', v]]]);
|
3400
|
+
}
|
3401
|
+
}
|
3402
|
+
// finalize
|
3195
3403
|
var newFunc = ['defun', newIdent, ['sp'], code];
|
3196
3404
|
var newAsmData = { params: { sp: ASM_INT }, vars: {} };
|
3197
3405
|
for (var v in codeInfo.reads) {
|
3198
|
-
newAsmData.vars[v] = getAsmType(
|
3406
|
+
if (v != 'sp') newAsmData.vars[v] = getAsmType(v, asmData);
|
3199
3407
|
}
|
3200
3408
|
for (var v in codeInfo.writes) {
|
3201
|
-
|
3409
|
+
assert(v != 'sp'); // we send sp as a read-only parameter, cannot be written to in outlined code
|
3410
|
+
newAsmData.vars[v] = getAsmType(v, asmData);
|
3202
3411
|
}
|
3203
3412
|
denormalizeAsm(newFunc, newAsmData);
|
3413
|
+
// add outline call markers (we cannot do later outlinings that cut through an outlining call)
|
3414
|
+
reps.unshift(['begin-outline-call', newIdent]);
|
3415
|
+
reps.push(['end-outline-call', newIdent]);
|
3204
3416
|
// replace in stats
|
3205
3417
|
stats.splice.apply(stats, [start, end-start+1].concat(reps));
|
3418
|
+
// final evaluation and processing
|
3419
|
+
if (!extraInfo.allowCostlyOutlines && (measureSize(func) >= funcSize || measureSize(newFunc) >= funcSize)) {
|
3420
|
+
//printErr('aborted outline attempt ' + [measureSize(func), measureSize(newFunc), ' one of which >= ', funcSize]);
|
3421
|
+
// abort, this was pointless
|
3422
|
+
stats.length = originalStats.length;
|
3423
|
+
for (var i = 0; i < stats.length; i++) stats[i] = originalStats[i];
|
3424
|
+
asmData.splitCounter--;
|
3425
|
+
return [];
|
3426
|
+
}
|
3427
|
+
for (var v in owned) {
|
3428
|
+
if (v != 'sp') delete asmData.vars[v]; // parent does not need these anymore
|
3429
|
+
}
|
3430
|
+
// if we just removed a final return from the original function, add one
|
3431
|
+
var last = getStatements(func)[getStatements(func).length-1];
|
3432
|
+
if (last[0] === 'stat') last = last[1];
|
3433
|
+
if (last[0] !== 'return') {
|
3434
|
+
if (allCodeInfo.hasReturnInt || allCodeInfo.hasReturnDouble) {
|
3435
|
+
getStatements(func).push(['stat', ['return', makeAsmCoercion(['num', 0], allCodeInfo.hasReturnInt ? ASM_INT : ASM_DOUBLE)]]);
|
3436
|
+
}
|
3437
|
+
}
|
3438
|
+
outliningParents[newIdent] = func[1];
|
3439
|
+
printErr('performed outline ' + [func[1], newIdent, 'code sizes (pre/post):', originalCodeSize, measureSize(code), 'overhead (w/r):', setSize(setSub(codeInfo.writes, owned)), setSize(setSub(codeInfo.reads, owned))]);
|
3440
|
+
calculateThreshold(func);
|
3206
3441
|
return [newFunc];
|
3207
3442
|
}
|
3208
3443
|
|
3209
3444
|
function outlineStatements(func, asmData, stats, maxSize) {
|
3210
3445
|
level++;
|
3211
|
-
|
3446
|
+
//printErr('outlineStatements: ' + [func[1], level, measureSize(func)]);
|
3447
|
+
var lastSize = measureSize(stats);
|
3448
|
+
if (lastSize < sizeToOutline) { level--; return }
|
3212
3449
|
var ret = [];
|
3213
3450
|
var sizeSeen = 0;
|
3214
3451
|
var end = stats.length-1;
|
3215
3452
|
var i = stats.length;
|
3216
|
-
|
3453
|
+
var canRestart = false;
|
3454
|
+
var minIndex = 0;
|
3455
|
+
function calcMinIndex() {
|
3456
|
+
if (stats == getStatements(func)) {
|
3457
|
+
minIndex = getFirstIndexInNormalized(func, asmData);
|
3458
|
+
for (var i = minIndex; i < stats.length; i++) {
|
3459
|
+
var stat = stats[i];
|
3460
|
+
if (stat[0] == 'stat') stat = stat[1];
|
3461
|
+
if (stat[0] == 'assign' && stat[2][0] == 'name' && stat[2][1] == 'sp') minIndex = i+1; // cannot outline |sp = |
|
3462
|
+
}
|
3463
|
+
}
|
3464
|
+
}
|
3465
|
+
while (1) {
|
3466
|
+
i--;
|
3467
|
+
calcMinIndex(); // TODO: optimize
|
3468
|
+
if (i < minIndex) {
|
3469
|
+
// we might be done. but, if we have just outlined, do a further attempt from the beginning.
|
3470
|
+
// (but only if the total costs are not extravagant)
|
3471
|
+
var currSize = measureSize(stats);
|
3472
|
+
var outlinedSize = measureSize(ret);
|
3473
|
+
if (canRestart && currSize > 1.2*sizeToOutline && lastSize - currSize >= 0.75*sizeToOutline) {
|
3474
|
+
//printErr('restarting ' + func[1] + ' since ' + [currSize, outlinedSize, lastSize] + ' in level ' + level);
|
3475
|
+
lastSize = currSize;
|
3476
|
+
i = stats.length;
|
3477
|
+
end = stats.length-1;
|
3478
|
+
sizeSeen = 0;
|
3479
|
+
canRestart = false;
|
3480
|
+
continue;
|
3481
|
+
} else {
|
3482
|
+
break;
|
3483
|
+
}
|
3484
|
+
}
|
3485
|
+
|
3217
3486
|
var stat = stats[i];
|
3487
|
+
while (stat[0] === 'end-outline-call') {
|
3488
|
+
// we cannot outline through an outline call, so include all of it
|
3489
|
+
while (stats[--i][0] !== 'begin-outline-call') {
|
3490
|
+
assert(i >= minIndex+1);
|
3491
|
+
assert(stats[i][0] !== 'end-outline-call');
|
3492
|
+
}
|
3493
|
+
stat = stats[i];
|
3494
|
+
}
|
3495
|
+
|
3218
3496
|
var size = measureSize(stat);
|
3219
3497
|
//printErr(level + ' size ' + [i, size]);
|
3220
3498
|
if (size >= sizeToOutline) {
|
3221
3499
|
// this by itself is big enough to inline, recurse into it and find statements to split on
|
3222
3500
|
var subStatements = null;
|
3501
|
+
var pre = ret.length;
|
3223
3502
|
traverse(stat, function(node, type) {
|
3224
3503
|
if (type == 'block') {
|
3225
3504
|
if (measureSize(node) >= sizeToOutline) {
|
@@ -3229,14 +3508,20 @@ function outline(ast) {
|
|
3229
3508
|
return null; // do not recurse into children, outlineStatements will do so if necessary
|
3230
3509
|
}
|
3231
3510
|
});
|
3232
|
-
|
3233
|
-
|
3511
|
+
if (ret.length > pre) {
|
3512
|
+
// we outlined recursively, reset our state here
|
3513
|
+
//printErr('successful outline in recursion ' + func[1] + ' due to recursive in level ' + level);
|
3514
|
+
end = i-1;
|
3515
|
+
sizeSeen = 0;
|
3516
|
+
canRestart = true;
|
3517
|
+
continue;
|
3518
|
+
}
|
3234
3519
|
}
|
3235
3520
|
sizeSeen += size;
|
3236
|
-
// If this is big enough to outline, but
|
3521
|
+
// If this is big enough to outline, but not too big (if very close to the size of the full function,
|
3237
3522
|
// outlining is pointless; remove stats from the end to try to achieve the good case), then outline.
|
3238
3523
|
// Also, try to reduce the size if it is much larger than the hoped-for size
|
3239
|
-
while ((sizeSeen > maxSize || sizeSeen > 2*sizeToOutline) && i
|
3524
|
+
while ((sizeSeen > maxSize || sizeSeen > 2*sizeToOutline) && end > i && stats[end][0] !== 'begin-outline-call' && stats[end][0] !== 'end-outline-call') {
|
3240
3525
|
sizeSeen -= measureSize(stats[end]);
|
3241
3526
|
if (sizeSeen >= sizeToOutline) {
|
3242
3527
|
end--;
|
@@ -3245,10 +3530,30 @@ function outline(ast) {
|
|
3245
3530
|
break;
|
3246
3531
|
}
|
3247
3532
|
}
|
3533
|
+
// verify we are not outlining through an outline call
|
3534
|
+
var sum = 0;
|
3535
|
+
stats.slice(i, end+1).forEach(function(stat) {
|
3536
|
+
if (stat[0] == 'begin-outline-call') {
|
3537
|
+
assert(sum == 0);
|
3538
|
+
sum++;
|
3539
|
+
} else if (stat[0] == 'end-outline-call') {
|
3540
|
+
assert(sum == 1);
|
3541
|
+
sum--;
|
3542
|
+
}
|
3543
|
+
});
|
3544
|
+
assert(sum == 0);
|
3545
|
+
// final decision and action
|
3546
|
+
//printErr(' will try done working on sizeSeen due to ' + [(sizeSeen > maxSize || sizeSeen > 2*sizeToOutline), end > i , stats[end][0] !== 'begin-outline-call' , stats[end][0] !== 'end-outline-call'] + ' ... ' + [sizeSeen, sizeToOutline, maxSize, sizeSeen >= sizeToOutline, sizeSeen <= maxSize]);
|
3248
3547
|
if (sizeSeen >= sizeToOutline && sizeSeen <= maxSize) {
|
3249
|
-
|
3548
|
+
assert(i >= minIndex);
|
3549
|
+
var newFuncs = doOutline(func, asmData, stats, i, end); // outline [i, .. ,end] inclusive
|
3550
|
+
if (newFuncs.length) {
|
3551
|
+
ret.push.apply(ret, newFuncs);
|
3552
|
+
}
|
3250
3553
|
sizeSeen = 0;
|
3251
3554
|
end = i-1;
|
3555
|
+
canRestart = true;
|
3556
|
+
continue;
|
3252
3557
|
}
|
3253
3558
|
}
|
3254
3559
|
level--;
|
@@ -3273,30 +3578,80 @@ function outline(ast) {
|
|
3273
3578
|
funcs.forEach(function(func) {
|
3274
3579
|
var asmData = normalizeAsm(func);
|
3275
3580
|
var size = measureSize(func);
|
3276
|
-
if (size >= sizeToOutline) {
|
3581
|
+
if (size >= extraInfo.sizeToOutline) {
|
3277
3582
|
aggressiveVariableElimination(func, asmData);
|
3583
|
+
flatten(func, asmData);
|
3584
|
+
calculateThreshold(func);
|
3278
3585
|
analyzeFunction(func, asmData);
|
3279
|
-
var
|
3280
|
-
|
3586
|
+
var stats = getStatements(func);
|
3587
|
+
var ret = outlineStatements(func, asmData, stats, 0.9*size);
|
3588
|
+
assert(level == 0);
|
3589
|
+
if (ret && ret.length > 0) {
|
3590
|
+
newFuncs.push.apply(newFuncs, ret);
|
3591
|
+
// We have outlined. Add stack support
|
3592
|
+
if ('sp' in asmData.vars) {
|
3593
|
+
// find stack bump (STACKTOP = STACKTOP + X | 0) and add the extra space
|
3594
|
+
var stackBumpNode = getStackBumpNode(stats);
|
3595
|
+
if (stackBumpNode) stackBumpNode[3][2][3][1] = asmData.totalStackSize;
|
3596
|
+
} else if (!('sp' in asmData.params)) { // if sp is a param, then we are an outlined function, no need to add stack support for us
|
3597
|
+
// add sp variable and stack bump
|
3598
|
+
var index = getFirstIndexInNormalized(func, asmData);
|
3599
|
+
stats.splice(index, 0,
|
3600
|
+
['stat', makeAssign(['name', 'sp'], ['name', 'STACKTOP'])],
|
3601
|
+
['stat', makeAssign(['name', 'STACKTOP'], ['binary', '|', ['binary', '+', ['name', 'STACKTOP'], ['num', asmData.totalStackSize]], ['num', 0]])]
|
3602
|
+
);
|
3603
|
+
asmData.vars.sp = ASM_INT; // no need to add to vars, we are about to denormalize anyhow
|
3604
|
+
// we added sp, so we must add stack popping
|
3605
|
+
function makePop() {
|
3606
|
+
return ['stat', makeAssign(['name', 'STACKTOP'], ['name', 'sp'])];
|
3607
|
+
}
|
3608
|
+
traverse(func, function(node, type) {
|
3609
|
+
var stats = getStatements(node);
|
3610
|
+
if (!stats) return;
|
3611
|
+
for (var i = 0; i < stats.length; i++) {
|
3612
|
+
var subNode = stats[i];
|
3613
|
+
if (subNode[0] === 'stat') subNode = subNode[1];
|
3614
|
+
if (subNode[0] == 'return') {
|
3615
|
+
stats.splice(i, 0, makePop());
|
3616
|
+
i++;
|
3617
|
+
}
|
3618
|
+
}
|
3619
|
+
});
|
3620
|
+
// pop the stack at the end if there is not a return
|
3621
|
+
var last = stats[stats.length-1];
|
3622
|
+
if (last[0] === 'stat') last = last[1];
|
3623
|
+
if (last[0] !== 'return') {
|
3624
|
+
stats.push(makePop());
|
3625
|
+
}
|
3626
|
+
}
|
3627
|
+
}
|
3628
|
+
printErr('... resulting size of ' + func[1] + ' is ' + measureSize(func));
|
3281
3629
|
}
|
3282
3630
|
denormalizeAsm(func, asmData);
|
3283
3631
|
});
|
3284
3632
|
|
3633
|
+
funcs = null;
|
3634
|
+
|
3285
3635
|
// TODO: control flow: route returns and breaks. outlined code should have all breaks/continues/returns break into the outermost scope,
|
3286
3636
|
// after setting a state variable, etc.
|
3287
3637
|
|
3288
3638
|
if (newFuncs.length > 0) {
|
3289
|
-
// We have outlined. Add stack support: header in which we allocate enough stack space TODO
|
3290
|
-
// If sp was not present before, add it and before each return, pop the stack. also a final pop if not ending with a return TODO
|
3291
|
-
// (none of this should be done in inner functions, of course, just the original)
|
3292
|
-
|
3293
3639
|
// add new functions to the toplevel, or create a toplevel if there isn't one
|
3294
3640
|
ast[1].push.apply(ast[1], newFuncs);
|
3295
3641
|
|
3296
|
-
|
3297
|
-
|
3642
|
+
// TODO: check if in some cases we do need to outline new functions
|
3643
|
+
//funcs = newFuncs.filter(function(newFunc) {
|
3644
|
+
// // recursively outline if we have a large new function that did not come at a high cost
|
3645
|
+
// return measureSize(newFunc) > sizeToOutline && costs[newFunc[1]] < 0.1*sizeToOutline;
|
3646
|
+
//});
|
3647
|
+
//more = funcs.length > 0;
|
3298
3648
|
}
|
3299
3649
|
}
|
3650
|
+
|
3651
|
+
// clear out markers
|
3652
|
+
traverse(ast, function(node, type) {
|
3653
|
+
if (type === 'begin-outline-call' || type === 'end-outline-call') return emptyNode();
|
3654
|
+
});
|
3300
3655
|
}
|
3301
3656
|
|
3302
3657
|
// Last pass utilities
|
@@ -3378,10 +3733,9 @@ var passes = {
|
|
3378
3733
|
unGlobalize: unGlobalize,
|
3379
3734
|
removeAssignsToUndefined: removeAssignsToUndefined,
|
3380
3735
|
//removeUnneededLabelSettings: removeUnneededLabelSettings,
|
3381
|
-
|
3736
|
+
simplifyExpressions: simplifyExpressions,
|
3382
3737
|
optimizeShiftsConservative: optimizeShiftsConservative,
|
3383
3738
|
optimizeShiftsAggressive: optimizeShiftsAggressive,
|
3384
|
-
simplifyExpressionsPost: simplifyExpressionsPost,
|
3385
3739
|
hoistMultiples: hoistMultiples,
|
3386
3740
|
loopOptimizer: loopOptimizer,
|
3387
3741
|
registerize: registerize,
|