webruby 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (258) hide show
  1. checksums.yaml +4 -4
  2. data/lib/webruby/rake/mruby.rake +1 -0
  3. data/modules/emscripten/AUTHORS +7 -0
  4. data/modules/emscripten/LICENSE +26 -0
  5. data/modules/emscripten/emcc +58 -20
  6. data/modules/emscripten/emlink.py +2 -265
  7. data/modules/emscripten/emscripten.py +38 -18
  8. data/modules/emscripten/scons-tools/emscripten.py +11 -6
  9. data/modules/emscripten/scons-tools/llvm.py +4 -3
  10. data/modules/emscripten/src/analyzer.js +32 -13
  11. data/modules/emscripten/src/embind/embind.js +13 -13
  12. data/modules/emscripten/src/embind/emval.js +8 -7
  13. data/modules/emscripten/src/intertyper.js +10 -0
  14. data/modules/emscripten/src/jsifier.js +28 -6
  15. data/modules/emscripten/src/library.js +2949 -1322
  16. data/modules/emscripten/src/library_browser.js +27 -23
  17. data/modules/emscripten/src/library_egl.js +7 -2
  18. data/modules/emscripten/src/library_gl.js +15 -2
  19. data/modules/emscripten/src/library_glut.js +1 -1
  20. data/modules/emscripten/src/library_jansson.js +1 -1
  21. data/modules/emscripten/src/library_openal.js +464 -132
  22. data/modules/emscripten/src/library_path.js +134 -0
  23. data/modules/emscripten/src/library_sdl.js +222 -50
  24. data/modules/emscripten/src/modules.js +22 -5
  25. data/modules/emscripten/src/parseTools.js +13 -2
  26. data/modules/emscripten/src/postamble.js +60 -34
  27. data/modules/emscripten/src/preamble.js +67 -18
  28. data/modules/emscripten/src/relooper/Relooper.cpp +1 -1
  29. data/modules/emscripten/src/runtime.js +12 -2
  30. data/modules/emscripten/src/settings.js +869 -826
  31. data/modules/emscripten/src/shell.js +63 -51
  32. data/modules/emscripten/src/utility.js +6 -0
  33. data/modules/emscripten/system/include/bsd/sys/mman.h +1 -1
  34. data/modules/emscripten/system/include/emscripten/bind.h +28 -28
  35. data/modules/emscripten/system/include/libc/math.h +8 -0
  36. data/modules/emscripten/system/include/libc/sys/signal.h +3 -1
  37. data/modules/emscripten/system/include/libc/sys/stat.h +2 -1
  38. data/modules/emscripten/system/include/libc/sys/types.h +4 -0
  39. data/modules/emscripten/system/include/libcxx/CREDITS.TXT +24 -0
  40. data/modules/emscripten/system/include/libcxx/__bit_reference +27 -8
  41. data/modules/emscripten/system/include/libcxx/__config +62 -15
  42. data/modules/emscripten/system/include/libcxx/__debug +5 -1
  43. data/modules/emscripten/system/include/libcxx/__functional_03 +24 -24
  44. data/modules/emscripten/system/include/libcxx/__functional_base +22 -4
  45. data/modules/emscripten/system/include/libcxx/__hash_table +566 -54
  46. data/modules/emscripten/system/include/libcxx/__locale +11 -3
  47. data/modules/emscripten/system/include/libcxx/__split_buffer +6 -6
  48. data/modules/emscripten/system/include/libcxx/__std_stream +58 -30
  49. data/modules/emscripten/system/include/libcxx/__tree +58 -51
  50. data/modules/emscripten/system/include/libcxx/__tuple +9 -9
  51. data/modules/emscripten/system/include/libcxx/algorithm +223 -13
  52. data/modules/emscripten/system/include/libcxx/array +18 -17
  53. data/modules/emscripten/system/include/libcxx/atomic +15 -5
  54. data/modules/emscripten/system/include/libcxx/cctype +2 -2
  55. data/modules/emscripten/system/include/libcxx/chrono +131 -36
  56. data/modules/emscripten/system/include/libcxx/cmath +41 -36
  57. data/modules/emscripten/system/include/libcxx/complex +49 -49
  58. data/modules/emscripten/system/include/libcxx/cstdio +2 -2
  59. data/modules/emscripten/system/include/libcxx/cstdlib +5 -5
  60. data/modules/emscripten/system/include/libcxx/cstring +2 -2
  61. data/modules/emscripten/system/include/libcxx/cwchar +22 -13
  62. data/modules/emscripten/system/include/libcxx/deque +27 -14
  63. data/modules/emscripten/system/include/libcxx/forward_list +36 -35
  64. data/modules/emscripten/system/include/libcxx/fstream +16 -0
  65. data/modules/emscripten/system/include/libcxx/functional +348 -23
  66. data/modules/emscripten/system/include/libcxx/future +66 -0
  67. data/modules/emscripten/system/include/libcxx/ios +27 -0
  68. data/modules/emscripten/system/include/libcxx/istream +2 -4
  69. data/modules/emscripten/system/include/libcxx/iterator +17 -9
  70. data/modules/emscripten/system/include/libcxx/limits +2 -2
  71. data/modules/emscripten/system/include/libcxx/list +165 -105
  72. data/modules/emscripten/system/include/libcxx/locale +154 -43
  73. data/modules/emscripten/system/include/libcxx/map +165 -224
  74. data/modules/emscripten/system/include/libcxx/memory +113 -54
  75. data/modules/emscripten/system/include/libcxx/random +2 -29
  76. data/modules/emscripten/system/include/libcxx/readme.txt +1 -1
  77. data/modules/emscripten/system/include/libcxx/regex +60 -15
  78. data/modules/emscripten/system/include/libcxx/sstream +124 -40
  79. data/modules/emscripten/system/include/libcxx/string +345 -182
  80. data/modules/emscripten/system/include/libcxx/support/win32/limits_win32.h +3 -3
  81. data/modules/emscripten/system/include/libcxx/support/win32/locale_win32.h +15 -2
  82. data/modules/emscripten/system/include/libcxx/support/win32/math_win32.h +3 -3
  83. data/modules/emscripten/system/include/libcxx/support/win32/support.h +10 -11
  84. data/modules/emscripten/system/include/libcxx/thread +2 -2
  85. data/modules/emscripten/system/include/libcxx/tuple +134 -65
  86. data/modules/emscripten/system/include/libcxx/type_traits +232 -24
  87. data/modules/emscripten/system/include/libcxx/unordered_map +314 -161
  88. data/modules/emscripten/system/include/libcxx/unordered_set +160 -2
  89. data/modules/emscripten/system/include/libcxx/utility +225 -40
  90. data/modules/emscripten/system/include/libcxx/vector +52 -57
  91. data/modules/emscripten/system/include/net/if.h +20 -1
  92. data/modules/emscripten/system/include/net/netinet/in.h +69 -5
  93. data/modules/emscripten/system/include/netdb.h +36 -0
  94. data/modules/emscripten/system/include/sys/ioctl.h +55 -3
  95. data/modules/emscripten/system/include/sys/select.h +2 -0
  96. data/modules/emscripten/system/include/sys/sendfile.h +16 -0
  97. data/modules/emscripten/system/include/sys/socket.h +181 -35
  98. data/modules/emscripten/system/lib/dlmalloc.c +10 -12
  99. data/modules/emscripten/system/lib/libc/musl/src/stdlib/ecvt.c +19 -0
  100. data/modules/emscripten/system/lib/libc/musl/src/stdlib/fcvt.c +25 -0
  101. data/modules/emscripten/system/lib/libc/musl/src/stdlib/gcvt.c +8 -0
  102. data/modules/emscripten/system/lib/libcextra.symbols +3 -0
  103. data/modules/emscripten/system/lib/libcxx/CREDITS.TXT +24 -0
  104. data/modules/emscripten/system/lib/libcxx/debug.cpp +11 -9
  105. data/modules/emscripten/system/lib/libcxx/exception.cpp +9 -0
  106. data/modules/emscripten/system/lib/libcxx/hash.cpp +6 -0
  107. data/modules/emscripten/system/lib/libcxx/iostream.cpp +4 -4
  108. data/modules/emscripten/system/lib/libcxx/locale.cpp +91 -42
  109. data/modules/emscripten/system/lib/libcxx/readme.txt +1 -1
  110. data/modules/emscripten/system/lib/libcxx/stdexcept.cpp +1 -1
  111. data/modules/emscripten/system/lib/libcxx/string.cpp +332 -491
  112. data/modules/emscripten/system/lib/libcxx/support/win32/locale_win32.cpp +4 -2
  113. data/modules/emscripten/system/lib/libcxx/support/win32/support.cpp +140 -41
  114. data/modules/emscripten/system/lib/libcxx/symbols +9 -256
  115. data/modules/emscripten/system/lib/libcxx/system_error.cpp +3 -0
  116. data/modules/emscripten/system/lib/libcxx/thread.cpp +16 -3
  117. data/modules/emscripten/system/lib/libcxx/typeinfo.cpp +12 -2
  118. data/modules/emscripten/third_party/stb_image.c +4673 -0
  119. data/modules/emscripten/tools/asm_module.py +273 -0
  120. data/modules/emscripten/tools/exec_llvm.py +2 -2
  121. data/modules/emscripten/tools/file_packager.py +36 -16
  122. data/modules/emscripten/tools/find_bigfuncs.py +9 -9
  123. data/modules/emscripten/tools/js-optimizer.js +485 -131
  124. data/modules/emscripten/tools/js_optimizer.py +22 -15
  125. data/modules/emscripten/tools/merge_asm.py +26 -0
  126. data/modules/emscripten/tools/nativize_llvm.py +2 -2
  127. data/modules/emscripten/tools/settings_template_readonly.py +1 -1
  128. data/modules/emscripten/tools/shared.py +63 -20
  129. data/modules/emscripten/tools/split_asm.py +30 -0
  130. data/modules/emscripten/tools/test-js-optimizer-asm-outline1-output.js +686 -0
  131. data/modules/emscripten/tools/test-js-optimizer-asm-outline1.js +263 -0
  132. data/modules/emscripten/tools/test-js-optimizer-asm-outline2-output.js +747 -0
  133. data/modules/emscripten/tools/{test-js-optimizer-asm-outline.js → test-js-optimizer-asm-outline2.js} +1 -1
  134. data/modules/emscripten/tools/test-js-optimizer-asm-outline3-output.js +28 -0
  135. data/modules/emscripten/tools/test-js-optimizer-asm-outline3.js +30 -0
  136. data/modules/emscripten/tools/test-js-optimizer-asm-pre-output.js +4 -4
  137. data/modules/mruby/AUTHORS +1 -0
  138. data/modules/mruby/README.md +4 -2
  139. data/modules/mruby/build_config.rb +6 -6
  140. data/modules/mruby/doc/mrbgems/README.md +4 -4
  141. data/modules/mruby/examples/mrbgems/c_and_ruby_extension_example/mrbgem.rake +1 -1
  142. data/modules/mruby/examples/mrbgems/c_extension_example/mrbgem.rake +1 -1
  143. data/modules/mruby/examples/mrbgems/ruby_extension_example/mrbgem.rake +1 -1
  144. data/modules/mruby/include/mrbconf.h +3 -0
  145. data/modules/mruby/include/mruby/array.h +2 -2
  146. data/modules/mruby/include/mruby/class.h +4 -4
  147. data/modules/mruby/include/mruby/compile.h +1 -0
  148. data/modules/mruby/include/mruby/data.h +1 -1
  149. data/modules/mruby/include/mruby/hash.h +2 -2
  150. data/modules/mruby/include/mruby/irep.h +3 -2
  151. data/modules/mruby/include/mruby/proc.h +1 -1
  152. data/modules/mruby/include/mruby/range.h +1 -1
  153. data/modules/mruby/include/mruby/string.h +2 -2
  154. data/modules/mruby/include/mruby/value.h +43 -26
  155. data/modules/mruby/include/mruby.h +10 -2
  156. data/modules/mruby/minirake +2 -2
  157. data/modules/mruby/mrbgems/mruby-array-ext/mrbgem.rake +1 -1
  158. data/modules/mruby/mrbgems/mruby-bin-mirb/mrbgem.rake +4 -1
  159. data/modules/mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c +32 -0
  160. data/modules/mruby/mrbgems/mruby-bin-mruby/mrbgem.rake +1 -1
  161. data/modules/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c +13 -5
  162. data/modules/mruby/mrbgems/mruby-enum-ext/mrbgem.rake +1 -1
  163. data/modules/mruby/mrbgems/mruby-enum-ext/test/enum.rb +10 -11
  164. data/modules/mruby/mrbgems/mruby-eval/mrbgem.rake +1 -1
  165. data/modules/mruby/mrbgems/mruby-exit/mrbgem.rake +4 -0
  166. data/modules/mruby/mrbgems/mruby-exit/src/mruby-exit.c +24 -0
  167. data/modules/mruby/mrbgems/mruby-fiber/mrbgem.rake +1 -1
  168. data/modules/mruby/mrbgems/mruby-fiber/src/fiber.c +4 -2
  169. data/modules/mruby/mrbgems/mruby-hash-ext/mrbgem.rake +1 -1
  170. data/modules/mruby/mrbgems/mruby-hash-ext/test/hash.rb +5 -7
  171. data/modules/mruby/mrbgems/mruby-math/mrbgem.rake +1 -1
  172. data/modules/mruby/mrbgems/mruby-numeric-ext/mrbgem.rake +1 -1
  173. data/modules/mruby/mrbgems/mruby-numeric-ext/test/numeric.rb +2 -2
  174. data/modules/mruby/mrbgems/mruby-object-ext/mrbgem.rake +1 -1
  175. data/modules/mruby/mrbgems/mruby-object-ext/src/object.c +3 -3
  176. data/modules/mruby/mrbgems/mruby-object-ext/test/nil.rb +3 -3
  177. data/modules/mruby/mrbgems/mruby-object-ext/test/object.rb +1 -1
  178. data/modules/mruby/mrbgems/mruby-objectspace/mrbgem.rake +1 -1
  179. data/modules/mruby/mrbgems/mruby-objectspace/test/objectspace.rb +36 -37
  180. data/modules/mruby/mrbgems/mruby-print/mrbgem.rake +1 -1
  181. data/modules/mruby/mrbgems/mruby-proc-ext/mrbgem.rake +1 -1
  182. data/modules/mruby/mrbgems/mruby-proc-ext/test/proc.rb +8 -8
  183. data/modules/mruby/mrbgems/mruby-random/mrbgem.rake +1 -1
  184. data/modules/mruby/mrbgems/mruby-range-ext/mrbgem.rake +1 -1
  185. data/modules/mruby/mrbgems/mruby-range-ext/test/range.rb +6 -6
  186. data/modules/mruby/mrbgems/mruby-sprintf/mrbgem.rake +1 -1
  187. data/modules/mruby/mrbgems/mruby-string-ext/mrbgem.rake +1 -1
  188. data/modules/mruby/mrbgems/mruby-string-ext/test/string.rb +6 -6
  189. data/modules/mruby/mrbgems/mruby-struct/mrbgem.rake +1 -1
  190. data/modules/mruby/mrbgems/mruby-symbol-ext/mrbgem.rake +1 -1
  191. data/modules/mruby/mrbgems/mruby-symbol-ext/test/symbol.rb +2 -2
  192. data/modules/mruby/mrbgems/mruby-time/mrbgem.rake +1 -1
  193. data/modules/mruby/mrbgems/mruby-time/src/time.c +2 -8
  194. data/modules/mruby/mrbgems/mruby-toplevel-ext/mrbgem.rake +1 -1
  195. data/modules/mruby/mrbgems/mruby-toplevel-ext/test/toplevel.rb +10 -10
  196. data/modules/mruby/mrblib/class.rb +15 -9
  197. data/modules/mruby/mrblib/string.rb +12 -0
  198. data/modules/mruby/src/array.c +4 -3
  199. data/modules/mruby/src/class.c +13 -8
  200. data/modules/mruby/src/codegen.c +9 -8
  201. data/modules/mruby/src/error.c +7 -5
  202. data/modules/mruby/src/error.h +1 -0
  203. data/modules/mruby/src/etc.c +1 -1
  204. data/modules/mruby/src/gc.c +163 -128
  205. data/modules/mruby/src/kernel.c +43 -15
  206. data/modules/mruby/src/numeric.c +9 -7
  207. data/modules/mruby/src/object.c +1 -1
  208. data/modules/mruby/src/parse.y +37 -19
  209. data/modules/mruby/src/range.c +10 -24
  210. data/modules/mruby/src/state.c +2 -6
  211. data/modules/mruby/src/string.c +0 -9
  212. data/modules/mruby/src/variable.c +2 -2
  213. data/modules/mruby/src/vm.c +12 -6
  214. data/modules/mruby/tasks/mrbgem_spec.rake +7 -0
  215. data/modules/mruby/tasks/mrbgems.rake +2 -1
  216. data/modules/mruby/tasks/mrbgems_test.rake +1 -1
  217. data/modules/mruby/tasks/mruby_build.rake +4 -3
  218. data/modules/mruby/tasks/mruby_build_commands.rake +6 -1
  219. data/modules/mruby/tasks/mruby_build_gem.rake +2 -2
  220. data/modules/mruby/tasks/toolchains/androideabi.rake +2 -0
  221. data/modules/mruby/test/assert.rb +2 -2
  222. data/modules/mruby/test/t/argumenterror.rb +3 -3
  223. data/modules/mruby/test/t/array.rb +55 -55
  224. data/modules/mruby/test/t/basicobject.rb +1 -1
  225. data/modules/mruby/test/t/bs_block.rb +12 -12
  226. data/modules/mruby/test/t/class.rb +21 -21
  227. data/modules/mruby/test/t/enumerable.rb +18 -18
  228. data/modules/mruby/test/t/exception.rb +20 -20
  229. data/modules/mruby/test/t/false.rb +3 -3
  230. data/modules/mruby/test/t/float.rb +40 -40
  231. data/modules/mruby/test/t/gc.rb +10 -10
  232. data/modules/mruby/test/t/hash.rb +41 -41
  233. data/modules/mruby/test/t/indexerror.rb +2 -2
  234. data/modules/mruby/test/t/integer.rb +41 -41
  235. data/modules/mruby/test/t/kernel.rb +33 -33
  236. data/modules/mruby/test/t/literals.rb +82 -82
  237. data/modules/mruby/test/t/localjumperror.rb +1 -1
  238. data/modules/mruby/test/t/module.rb +170 -31
  239. data/modules/mruby/test/t/nameerror.rb +5 -5
  240. data/modules/mruby/test/t/nil.rb +2 -2
  241. data/modules/mruby/test/t/nomethoderror.rb +1 -1
  242. data/modules/mruby/test/t/numeric.rb +5 -5
  243. data/modules/mruby/test/t/object.rb +2 -2
  244. data/modules/mruby/test/t/proc.rb +8 -8
  245. data/modules/mruby/test/t/range.rb +9 -9
  246. data/modules/mruby/test/t/rangeerror.rb +2 -2
  247. data/modules/mruby/test/t/runtimeerror.rb +1 -1
  248. data/modules/mruby/test/t/standarderror.rb +2 -2
  249. data/modules/mruby/test/t/string.rb +100 -100
  250. data/modules/mruby/test/t/symbol.rb +5 -5
  251. data/modules/mruby/test/t/syntax.rb +15 -6
  252. data/modules/mruby/test/t/true.rb +3 -3
  253. data/modules/mruby/test/t/typeerror.rb +2 -2
  254. data/modules/mruby/tools/mrbc/mrbc.c +10 -4
  255. data/modules/mruby/travis_config.rb +1 -0
  256. data/scripts/gen_gems_config.rb +5 -1
  257. metadata +19 -4
  258. 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 simplifyExpressionsPre(ast) {
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 makeAsmParamCoercion(param, type) {
1551
- return type === ASM_INT ? ['binary', '|', ['name', param], ['num', 0]] : ['unary-prefix', '+', ['name', param]];
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(asmInfo, name) {
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], makeAsmParamCoercion(param, data.params[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
- assert(fun[1] in extraInfo.globals, fun[1]);
1762
- fun[1] = extraInfo.globals[fun[1]];
1763
- assert(fun[1]);
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] += base;
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
- asmData.stackSize = (stack.length + 2)*8;
2989
- asmData.controlStackPos = asmData.stackSize - 16;
2990
- asmData.controlDataStackPos = asmData.stackSize - 8;
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 ((type == 'label' || type in LOOP_FLOW) && node[1] && !(node[1] in labels)) {
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 appearances = {};
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. '0' is an unlabeled one
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
- appearances[name] = (appearances[name] || 0) + 1;
3144
+ namings[name] = (namings[name] || 0) + 1;
3024
3145
  }
3025
3146
  } else if (type == 'return') {
3026
- hasReturn = true;
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 || 0] = 0;
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 || 0] = 0;
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
- for (var name in appearances) {
3059
- if (appearances[name] > 0) reads[name] = 0;
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 = extraInfo.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
- printErr(' do outline ' + [func[1], level, 'range:', start, end, 'of', stats.length]);
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 newIdent = func[1] + '$' + (asmData.splitCounter++);
3089
- // add spills and reads before and after the call to the outlined code, and in the outlined code itself
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
- for (var v in codeInfo.reads) {
3093
- if (v != 'sp') {
3094
- reps.push(['stat', ['assign', true, ['sub', ['name', getAsmType(asmData, v) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], ['name', v]]]);
3095
- code.unshift(['stat', ['assign', true, ['name', v], ['sub', ['name', getAsmType(asmData, v) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]]]]);
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
- reps.push(['stat', ['assign', true, ['name', v], ['sub', ['name', getAsmType(asmData, v) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]]]]);
3101
- code.push(['stat', ['assign', true, ['sub', ['name', getAsmType(asmData, v) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], ['name', v]]]);
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 == 'return') {
3112
- var ret = ['break', 'OL'];
3113
- if (!node[1]) {
3114
- ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', CONTROL_RETURN_VOID]), ret];
3115
- } else {
3116
- var type = detectAsmCoercion(node[1], asmData);
3117
- ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', type == ASM_INT ? CONTROL_RETURN_INT : CONTROL_RETURN_DOUBLE]), ret];
3118
- ret = ['seq', makeAssign(makeStackAccess(type, asmData.controlDataStackPos), node[1]), ret];
3119
- }
3120
- return ret;
3121
- } else if (type == 'break') {
3122
- var label = node[1] || 0;
3123
- if (label == 'OL') return; // this was just added before us, it is new replacement code
3124
- if (!label && breakCapturers > 0) return; // no label, and captured
3125
- if (label && (label in codeInfo.labels)) return; // label, and defined in this code, so captured
3126
- var ret = ['break', 'OL'];
3127
- ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', label ? CONTROL_BREAK_LABEL : CONTROL_BREAK]), ret];
3128
- if (label) {
3129
- assert(label in codeInfo.labels, label + ' in ' + keys(codeInfo.labels));
3130
- ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos), ['num', codeInfo.labels[label]]), ret];
3131
- }
3132
- return ret;
3133
- } else if (type == 'continue') {
3134
- var label = node[1] || 0;
3135
- if (!label && continueCapturers > 0) return; // no label, and captured
3136
- if (label && (label in codeInfo.labels)) return; // label, and defined in this code, so captured
3137
- var ret = ['break', 'OL'];
3138
- ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', label ? CONTROL_CONTINUE_LABEL : CONTROL_CONTINUE]), ret];
3139
- if (label) {
3140
- ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos), ['num', codeInfo.labels[label]]), ret];
3141
- }
3142
- return ret;
3143
- } else {
3144
- if (type in BREAK_CAPTURERS) {
3145
- breakCapturers++;
3146
- }
3147
- if (type in CONTINUE_CAPTURERS) {
3148
- continueCapturers++;
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
- // read the control data at the callsite to the outlined function
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(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_RETURN_VOID]),
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(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_RETURN_INT]),
3167
- [['stat', ['return', makeStackAccess(ASM_INT, asmData.controlDataStackPos)]]]
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(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_RETURN_DOUBLE]),
3171
- [['stat', ['return', makeStackAccess(ASM_DOUBLE, asmData.controlDataStackPos)]]]
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(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_BREAK]),
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(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_CONTINUE]),
3187
- ['stat', ['break']]
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(asmData, v);
3406
+ if (v != 'sp') newAsmData.vars[v] = getAsmType(v, asmData);
3199
3407
  }
3200
3408
  for (var v in codeInfo.writes) {
3201
- newAsmData.vars[v] = getAsmType(asmData, v);
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
- if (measureSize(stats) < sizeToOutline) return;
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
- while (--i >= 0) {
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
- sizeSeen = 0;
3233
- continue;
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 no too big (if very close to the size of the full function,
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 < end) {
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
- ret.push.apply(ret, doOutline(func, asmData, stats, i, end)); // outline [i, .. ,end] inclusive
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 ret = outlineStatements(func, asmData, getStatements(func), 0.5*size);
3280
- if (ret && ret.length > 0) newFuncs.push.apply(newFuncs, ret);
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
- funcs = newFuncs;
3297
- more = true;
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
- simplifyExpressionsPre: simplifyExpressionsPre,
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,