From 2a3e84086e1c28589e715b5501f008f803e3bc93 Mon Sep 17 00:00:00 2001
From: Usagi Ito <usagi@WonderRabbitProject.net>
Date: Wed, 10 Dec 2014 13:08:05 +0900
Subject: [PATCH 01/31] fix #3066 glfwGetCursorPos parameter typing bug

1. fix `glfwGetCursorPos`(GLFW3 API):
    -  parameter type from 'i32' to 'double'.
2. add `GLFW.getMousePos`:
    - `i32` version for GLFW2.
3. fix `glfwGetMousePos`(GLFW2 API):
    - from call `GLFW.getCursorPos` to call `GLFW.getMousePos`
---
 src/library_glfw.js | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/library_glfw.js b/src/library_glfw.js
index 7998022485c9b..4a44a55b4da27 100644
--- a/src/library_glfw.js
+++ b/src/library_glfw.js
@@ -527,6 +527,11 @@ var LibraryGLFW = {
     },
 
     getCursorPos: function(winid, x, y) {
+      setValue(x, Browser.mouseX, 'double');
+      setValue(y, Browser.mouseY, 'double');
+    },
+
+    getMousePos: function(winid, x, y) {
       setValue(x, Browser.mouseX, 'i32');
       setValue(y, Browser.mouseY, 'i32');
     },
@@ -1145,7 +1150,7 @@ var LibraryGLFW = {
   },
 
   glfwGetMousePos: function(x, y) {
-    GLFW.getCursorPos(GLFW.active.id, x, y);
+    GLFW.getMousePos(GLFW.active.id, x, y);
   },
 
   glfwSetMousePos: function(x, y) {

From 84bbf2a6e2c84de9576976e40c7d56e17eb79fb1 Mon Sep 17 00:00:00 2001
From: Bruce Mitchener <bruce.mitchener@gmail.com>
Date: Wed, 10 Dec 2014 14:50:31 +0700
Subject: [PATCH 02/31] Disable warning when asm.js validation is already
 disabled.

When ASM_JS is already 2, no need to warn about allowing memory growth
as the user must have known about this and set ASM_JS to 2.
---
 emcc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/emcc b/emcc
index a78fa890131c2..f412ccb8d452a 100755
--- a/emcc
+++ b/emcc
@@ -932,7 +932,7 @@ try:
       js_opts = True
       logging.warning('enabling js opts to allow SAFE_HEAP to work properly')
 
-    if shared.Settings.ALLOW_MEMORY_GROWTH:
+    if shared.Settings.ALLOW_MEMORY_GROWTH and shared.Settings.ASM_JS != 2:
       logging.warning('Disabling asm.js validation for memory growth (memory can grow, but you lose some amount of speed)');
       shared.Settings.ASM_JS = 2
 

From c884a4be5fa3caa05e53eed631684288799aa44a Mon Sep 17 00:00:00 2001
From: Bruce Mitchener <bruce.mitchener@gmail.com>
Date: Wed, 10 Dec 2014 15:03:02 +0700
Subject: [PATCH 03/31] Set ASM_JS default prior to processing command line.

This lets the user set ASM_JS to 2 and avoid a warning if they
also set ALLOW_MEMORY_GROWTH.
---
 emcc | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/emcc b/emcc
index f412ccb8d452a..fd4e4c75ff6cd 100755
--- a/emcc
+++ b/emcc
@@ -860,6 +860,11 @@ try:
   # Apply optimization level settings
   shared.Settings.apply_opt_level(opt_level, noisy=True)
 
+  fastcomp = os.environ.get('EMCC_FAST_COMPILER') != '0'
+  if fastcomp:
+    # Set ASM_JS default here so that we can override it from the command line.
+    shared.Settings.ASM_JS = 1 if opt_level > 0 else 2
+
   # Apply -s settings in newargs here (after optimization levels, so they can override them)
   for change in settings_changes:
     key, value = change.split('=')
@@ -871,11 +876,9 @@ try:
     if key == 'EXPORTED_FUNCTIONS':
       shared.Settings.ORIGINAL_EXPORTED_FUNCTIONS = shared.Settings.EXPORTED_FUNCTIONS[:] # used for warnings in emscripten.py
 
-  fastcomp = os.environ.get('EMCC_FAST_COMPILER') != '0'
-
   if fastcomp:
-    shared.Settings.ASM_JS = 1 if opt_level > 0 else 2
     try:
+      assert shared.Settings.ASM_JS > 0, 'ASM_JS must be enabled in fastcomp'
       assert shared.Settings.UNALIGNED_MEMORY == 0, 'forced unaligned memory not supported in fastcomp'
       assert shared.Settings.CHECK_HEAP_ALIGN == 0, 'check heap align not supported in fastcomp - use SAFE_HEAP instead'
       assert shared.Settings.SAFE_DYNCALLS == 0, 'safe dyncalls not supported in fastcomp'

From c59dab8d49ce4ff6615c39b7d39ae7fefd27e8b3 Mon Sep 17 00:00:00 2001
From: Gauthier Billot <gogoprog@gmail.com>
Date: Wed, 10 Dec 2014 14:13:14 +0100
Subject: [PATCH 04/31] Fixed wrong variable name in library_sdl.js

---
 src/library_sdl.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/library_sdl.js b/src/library_sdl.js
index befa4e4b32c9d..839f7f1b2f6a4 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -491,7 +491,7 @@ var LibrarySDL = {
           SDL.updateRect(dstrect, dr);
         }
       }
-      var blitw, blitr;
+      var blitw, blith;
       if (scale) {
         blitw = dr.w; blith = dr.h;
       } else {

From 5e7e1ae50e3757601c32c8aa2c52734cf0459e6e Mon Sep 17 00:00:00 2001
From: Victor Costan <costan@gmail.com>
Date: Wed, 10 Dec 2014 13:04:51 -0500
Subject: [PATCH 05/31] Make resize in std::vector bindings not assume a
 default constructor.

This change uses the std::vector::resize variant that takes a value
argument and uses it if the vector needs to be extended. The previous
attempt of adding resize used the 1 argument version, which required
that the vector's element type has a default constructor.
---
 system/include/emscripten/bind.h |  2 +-
 tests/embind/embind.test.js      | 10 +++++-----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h
index 1ec344927b106..7f9de9ab8404c 100644
--- a/system/include/emscripten/bind.h
+++ b/system/include/emscripten/bind.h
@@ -1379,7 +1379,7 @@ namespace emscripten {
         typedef std::vector<T> VecType;
 
         void (VecType::*push_back)(const T&) = &VecType::push_back;
-        void (VecType::*resize)(const size_t) = &VecType::resize;
+        void (VecType::*resize)(const size_t, const T&) = &VecType::resize;
         return class_<std::vector<T>>(name)
             .template constructor<>()
             .function("push_back", push_back)
diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js
index 1b29c5ad79755..141f5bc747fd0 100644
--- a/tests/embind/embind.test.js
+++ b/tests/embind/embind.test.js
@@ -918,23 +918,23 @@ module({
             vec.delete();
         });
 
-        test("resize appends the default value", function() {
+        test("resize appends the given value", function() {
             var vec = cm.emval_test_return_vector();
 
-            vec.resize(5);
+            vec.resize(5, 42);
             assert.equal(5, vec.size());
             assert.equal(10, vec.get(0));
             assert.equal(20, vec.get(1));
             assert.equal(30, vec.get(2));
-            assert.equal(0, vec.get(3));
-            assert.equal(0, vec.get(4));
+            assert.equal(42, vec.get(3));
+            assert.equal(42, vec.get(4));
             vec.delete();
         });
 
         test("resize preserves content when shrinking", function() {
             var vec = cm.emval_test_return_vector();
 
-            vec.resize(2);
+            vec.resize(2, 42);
             assert.equal(2, vec.size());
             assert.equal(10, vec.get(0));
             assert.equal(20, vec.get(1));

From 5e32b716b04b8fad5d451498da070b31d5c672cd Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Wed, 10 Dec 2014 13:34:21 -0800
Subject: [PATCH 06/31] refactoring in preparation for conversion of
 registerizeHarder

---
 tools/optimizer/optimizer.cpp | 1121 ++++++++++++++++++++++++++++++++-
 1 file changed, 1105 insertions(+), 16 deletions(-)

diff --git a/tools/optimizer/optimizer.cpp b/tools/optimizer/optimizer.cpp
index b5f7ddfff71e0..0ed2e13245f68 100644
--- a/tools/optimizer/optimizer.cpp
+++ b/tools/optimizer/optimizer.cpp
@@ -2189,6 +2189,32 @@ void optimizeFrounds(Ref ast) {
 }
 
 // Very simple 'registerization', coalescing of variables into a smaller number.
+
+const char* getRegPrefix(AsmType type) {
+  switch (type) {
+    case ASM_INT:       return"i"; break;
+    case ASM_DOUBLE:    return "d"; break;
+    case ASM_FLOAT:     return "f"; break;
+    case ASM_FLOAT32X4: return "F4"; break;
+    case ASM_INT32X4:   return "I4"; break;
+    case ASM_NONE:      return "Z"; break;
+    default: assert(0); // type doesn't have a name yet
+  }
+  return nullptr;
+}
+
+IString getRegName(AsmType type, int num) {
+  const char* str = getRegPrefix(type);
+  int size = strlen(str) + int(ceil(log10(num))) + 3;
+  char temp[size];
+  int written = sprintf(temp, "%s%d", str, num);
+  assert(written < size);
+  temp[written] = 0;
+  IString ret;
+  ret.set(temp, false);
+  return ret;
+}
+
 void registerize(Ref ast) {
   traverseFunctions(ast, [](Ref fun) {
     AsmData asmData(fun);
@@ -2222,23 +2248,9 @@ void registerize(Ref ast) {
     auto getNewRegName = [&](int num, IString name) {
       const char *str;
       AsmType type = asmData.getType(name);
-      switch (type) {
-        case ASM_INT:       str = "i"; break;
-        case ASM_DOUBLE:    str = "d"; break;
-        case ASM_FLOAT:     str = "f"; break;
-        case ASM_FLOAT32X4: str = "F4"; break;
-        case ASM_INT32X4:   str = "I4"; break;
-        case ASM_NONE:      str = "Z"; break;
-        default: assert(0); // type doesn't have a name yet
-      }
-      int size = strlen(str) + int(ceil(log10(num))) + 3;
-      char *temp = (char*)malloc(size);
-      int written = sprintf(temp, "%s%d", str, num);
-      assert(written < size);
-      temp[written] = 0;
-      IString ret(temp); // likely interns a new string; leaks if not XXX FIXME
-      regTypes[ret] = type;
+      IString ret = getRegName(type, num);
       assert(!allVars.has(ret) || asmData.isLocal(ret)); // register must not shadow non-local name
+      regTypes[ret] = type;
       return ret;
     };
     // Find the # of uses of each variable.
@@ -2433,6 +2445,1082 @@ void registerize(Ref ast) {
   });
 }
 
+// Assign variables to 'registers', coalescing them onto a smaller number of shared
+// variables.
+//
+// This does the same job as 'registerize' above, but burns a lot more cycles trying
+// to reduce the total number of register variables.  Key points about the operation:
+//
+//   * we decompose the AST into a flow graph and perform a full liveness
+//     analysis, to determine which variables are live at each point.
+//
+//   * variables that are live concurrently are assigned to different registers.
+//
+//   * variables that are linked via 'x=y' style statements are assigned the same
+//     register if possible, so that the redundant assignment can be removed.
+//     (e.g. assignments used to pass state around through loops).
+//
+//   * any code that cannot be reached through the flow-graph is removed.
+//     (e.g. redundant break statements like 'break L123; break;').
+//
+//   * any assignments that we can prove are not subsequently used are removed.
+//     (e.g. unnecessary assignments to the 'label' variable).
+//
+/*
+void registerizeHarder(Ref ast) {
+  traverseFunctions(ast, [](Ref fun) {
+
+    // Do not try to process non-validating methods, like the heap replacer
+    bool abort = false;
+    traverse(fun, function(node, type) {
+      if (type === NEW) abort = true;
+    });
+    if (abort) return;
+
+    AsmData asmData(fun);
+
+    // Utilities for allocating register variables.
+    // We need distinct register pools for each type of variable.
+
+    std::vector<StringStringMap> allRegsByType;
+    allRegsByType.resize(ASM_NONE+1);
+    int nextReg = 1;
+
+    auto createReg = [&](IString forName) {
+      // Create a new register of type suitable for the given variable name.
+      AsmType type = asmData.getType(forName);
+      StringIntMap& allRegs = allRegsByType[type];
+      reg = nextReg++;
+      allRegs[reg] = getRegName(type, reg);
+      return reg;
+    };
+
+    // Traverse the tree in execution order and synthesize a basic flow-graph.
+    // It's convenient to build a kind of "dual" graph where the nodes identify
+    // the junctions between blocks  at which control-flow may branch, and each
+    // basic block is an edge connecting two such junctions.
+    // For each junction we store:
+    //    * set of blocks that originate at the junction
+    //    * set of blocks that terminate at the junction
+    // For each block we store:
+    //    * a single entry junction
+    //    * a single exit junction
+    //    * a 'use' and 'kill' set of names for the block
+    //    * full sequence of 'name' and 'assign' nodes in the block
+    //    * whether each such node appears as part of a larger expression
+    //      (and therefore cannot be safely eliminated)
+    //    * set of labels that can be used to jump to this block
+
+    var junctions = [];
+    var blocks = [];
+    var currEntryJunction = null;
+    var nextBasicBlock = null;
+    var isInExpr = 0;
+    var activeLabels = [{}];
+    var nextLoopLabel = null;
+
+    var ENTRY_JUNCTION = 0;
+    var EXIT_JUNCTION = 1;
+    var ENTRY_BLOCK = 0;
+
+    function addJunction() {
+      // Create a new junction, without inserting it into the graph.
+      // This is useful for e.g. pre-allocating an exit node.
+      var id = junctions.length;
+      junctions[id] = {id: id, inblocks: {}, outblocks: {}};
+      return id;
+    }
+
+    function markJunction(id) {
+      // Mark current traversal location as a junction.
+      // This makes a new basic block exiting at this position.
+      if (id === undefined || id === null) {
+        id = addJunction();
+      }
+      joinJunction(id, true);
+      return id;
+    }
+
+    function setJunction(id, force) {
+      // Set the next entry junction to the given id.
+      // This can be used to enter at a previously-declared point.
+      // You can't return to a junction with no incoming blocks
+      // unless the 'force' parameter is specified.
+      assert(nextBasicBlock.nodes.length === 0, 'refusing to abandon an in-progress basic block')
+      if (force || setSize(junctions[id].inblocks) > 0) {
+        currEntryJunction = id;
+      } else {
+        currEntryJunction = null;
+      }
+    }
+
+    function joinJunction(id, force) {
+      // Complete the pending basic block by exiting at this position.
+      // This can be used to exit at a previously-declared point.
+      if (currEntryJunction !== null) {
+        nextBasicBlock.id = blocks.length;
+        nextBasicBlock.entry = currEntryJunction;
+        nextBasicBlock.exit = id;
+        junctions[currEntryJunction].outblocks[nextBasicBlock.id] = 1;
+        junctions[id].inblocks[nextBasicBlock.id] = 1;
+        blocks.push(nextBasicBlock);
+      } 
+      nextBasicBlock = { id: null, entry: null, exit: null, labels: {}, nodes: [], isexpr: [], use: {}, kill: {} };
+      setJunction(id, force);
+      return id;
+    }
+
+    function pushActiveLabels(onContinue, onBreak) {
+      // Push the target junctions for continuing/breaking a loop.
+      // This should be called before traversing into a loop.
+      var prevLabels = activeLabels[activeLabels.length-1];
+      var newLabels = copy(prevLabels);
+      newLabels[null] = [onContinue, onBreak];
+      if (nextLoopLabel) {
+        newLabels[nextLoopLabel] = [onContinue, onBreak];
+        nextLoopLabel = null;
+      }
+      // An unlabelled 'continue' should jump to innermost loop,
+      // ignoring any nested 'switch' statements.
+      if (onContinue === null && prevLabels[null]) {
+        newLabels[null][0] = prevLabels[null][0];
+      }
+      activeLabels.push(newLabels);
+    }
+
+    function popActiveLabels() {
+      // Pop the target junctions for continuing/breaking a loop.
+      // This should be called after traversing into a loop.
+      activeLabels.pop();
+    }
+
+    function markNonLocalJump(type, label) {
+      // Complete a block via  'return', 'break' or 'continue'.
+      // This joins the targetted junction and then sets the current junction to null.
+      // Any code traversed before we get back an existing junction is dead code.
+      if (type === 'return') {
+        joinJunction(EXIT_JUNCTION);
+      } else {
+        label = label ? label : null;
+        var targets = activeLabels[activeLabels.length-1][label];
+        assert(targets, 'jump to unknown label');
+        if (type === 'continue') {
+          joinJunction(targets[0]);
+        } else if (type === 'break') {
+          joinJunction(targets[1]);
+        } else {
+          assert(false, 'unknown jump node type');
+        }
+      }
+      currEntryJunction = null;
+    }
+
+    function addUseNode(node) {
+      // Mark a use of the given name node in the current basic block.
+      assert(node[0] === 'name', 'not a use node');
+      var name = node[1];
+      if (name in localVars) {
+        nextBasicBlock.nodes.push(node);
+        nextBasicBlock.isexpr.push(isInExpr);
+        if (!nextBasicBlock.kill[name]) {
+          nextBasicBlock.use[name] = 1;
+        }
+      }
+    }
+
+    function addKillNode(node) {
+      // Mark an assignment to the given name node in the current basic block.
+      assert(node[0] === 'assign', 'not a kill node');
+      assert(node[1] === true, 'not a kill node');
+      assert(node[2][0] === 'name', 'not a kill node');
+      var name = node[2][1];
+      if (name in localVars) {
+        nextBasicBlock.nodes.push(node);
+        nextBasicBlock.isexpr.push(isInExpr);
+        nextBasicBlock.kill[name] = 1;
+      }
+    }
+
+    function lookThroughCasts(node) {
+      // Look through value-preserving casts, like "x | 0" => "x"
+      if (node[0] === 'binary' && node[1] === '|') {
+        if (node[3][0] === 'num' && node[3][1] === 0) {
+            return lookThroughCasts(node[2]);
+        }
+      }
+      return node;
+    }
+
+    function addBlockLabel(node) {
+      assert(nextBasicBlock.nodes.length === 0, 'cant add label to an in-progress basic block')
+      if (node[0] === 'num') {
+        nextBasicBlock.labels[node[1]] = 1;
+      }
+    }
+
+    function isTrueNode(node) {
+      // Check if the given node is statically truthy.
+      return (node[0] === 'num' && node[1] != 0);
+    }
+
+    function isFalseNode(node) {
+      // Check if the given node is statically falsy.
+      return (node[0] === 'num' && node[1] == 0);
+    }
+
+    function morphNode(node, newNode) {
+      // In-place morph a node into some other type of node.
+      var i = 0;
+      while (i < node.length && i < newNode.length) {
+        node[i] = newNode[i];
+        i++;
+      }
+      while (i < newNode.length) {
+        node.push(newNode[i]);
+        i++;
+      }
+      if (node.length > newNode.length) {
+        node.length = newNode.length;
+      }
+    }
+
+    function buildFlowGraph(node) {
+      // Recursive function to build up the flow-graph.
+      // It walks the tree in execution order, calling the above state-management
+      // functions at appropriate points in the traversal.
+      var type = node[0];
+  
+      // Any code traversed without an active entry junction must be dead,
+      // as the resulting block could never be entered. Let's remove it.
+      if (currEntryJunction === null && junctions.length > 0) {
+        morphNode(node, ['block', []]);
+        return;
+      }
+ 
+      // Traverse each node type according to its particular control-flow semantics.
+      switch (type) {
+        case 'defun':
+          var jEntry = markJunction();
+          assert(jEntry === ENTRY_JUNCTION);
+          var jExit = addJunction();
+          assert(jExit === EXIT_JUNCTION);
+          for (var i = 0; i < node[3].length; i++) {
+            buildFlowGraph(node[3][i]);
+          }
+          joinJunction(jExit);
+          break;
+        case 'if':
+          isInExpr++;
+          buildFlowGraph(node[1]);
+          isInExpr--;
+          var jEnter = markJunction();
+          var jExit = addJunction();
+          if (node[2]) {
+            // Detect and mark "if (label == N) { <labelled block> }".
+            if (node[1][0] === 'binary' && node[1][1] === '==') {
+              var lhs = lookThroughCasts(node[1][2]);
+              if (lhs[0] === 'name' && lhs[1] === 'label') {
+                addBlockLabel(lookThroughCasts(node[1][3]));
+              }
+            }
+            buildFlowGraph(node[2]);
+          }
+          joinJunction(jExit);
+          setJunction(jEnter);
+          if (node[3]) {
+            buildFlowGraph(node[3]);
+          }
+          joinJunction(jExit);
+          break;
+        case 'conditional':
+          isInExpr++;
+          // If the conditional has no side-effects, we can treat it as a single
+          // block, which might open up opportunities to remove it entirely.
+          if (!hasSideEffects(node)) {
+            buildFlowGraph(node[1]);
+            if (node[2]) {
+              buildFlowGraph(node[2]);
+            }
+            if (node[3]) {
+              buildFlowGraph(node[3]);
+            }
+          } else {
+            buildFlowGraph(node[1]);
+            var jEnter = markJunction();
+            var jExit = addJunction();
+            if (node[2]) {
+              buildFlowGraph(node[2]);
+            }
+            joinJunction(jExit);
+            setJunction(jEnter);
+            if (node[3]) {
+              buildFlowGraph(node[3]);
+            }
+            joinJunction(jExit);
+          }
+          isInExpr--;
+          break;
+        case 'while':
+          // Special-case "while (1) {}" to use fewer junctions,
+          // since emscripten generates a lot of these.
+          if (isTrueNode(node[1])) {
+            var jLoop = markJunction();
+            var jExit = addJunction();
+            pushActiveLabels(jLoop, jExit);
+            buildFlowGraph(node[2]);
+            popActiveLabels();
+            joinJunction(jLoop);
+            setJunction(jExit);
+          } else {
+            var jCond = markJunction();
+            var jLoop = addJunction();
+            var jExit = addJunction();
+            isInExpr++;
+            buildFlowGraph(node[1]);
+            isInExpr--;
+            joinJunction(jLoop);
+            pushActiveLabels(jCond, jExit);
+            buildFlowGraph(node[2]);
+            popActiveLabels();
+            joinJunction(jCond);
+            // An empty basic-block linking condition exit to loop exit.
+            setJunction(jLoop);
+            joinJunction(jExit);
+          }
+          break;
+        case 'do':
+          // Special-case "do {} while (1)" and "do {} while (0)" to use
+          // fewer junctions, since emscripten generates a lot of these.
+          if (isFalseNode(node[1])) {
+            var jExit = addJunction();
+            pushActiveLabels(jExit, jExit);
+            buildFlowGraph(node[2]);
+            popActiveLabels();
+            joinJunction(jExit);
+          } else if (isTrueNode(node[1])) {
+            var jLoop = markJunction();
+            var jExit = addJunction();
+            pushActiveLabels(jLoop, jExit);
+            buildFlowGraph(node[2]);
+            popActiveLabels();
+            joinJunction(jLoop);
+            setJunction(jExit);
+          } else {
+            var jLoop = markJunction();
+            var jCond = addJunction();
+            var jCondExit = addJunction();
+            var jExit = addJunction();
+            pushActiveLabels(jCond, jExit);
+            buildFlowGraph(node[2]);
+            popActiveLabels();
+            joinJunction(jCond);
+            isInExpr++;
+            buildFlowGraph(node[1]);
+            isInExpr--;
+            joinJunction(jCondExit);
+            joinJunction(jLoop);
+            setJunction(jCondExit);
+            joinJunction(jExit)
+          }
+          break;
+        case 'for':
+          var jTest = addJunction();
+          var jBody = addJunction();
+          var jStep = addJunction();
+          var jExit = addJunction();
+          buildFlowGraph(node[1]);
+          joinJunction(jTest);
+          isInExpr++;
+          buildFlowGraph(node[2]);
+          isInExpr--;
+          joinJunction(jBody);
+          pushActiveLabels(jStep, jExit);
+          buildFlowGraph(node[4]);
+          popActiveLabels();
+          joinJunction(jStep);
+          buildFlowGraph(node[3]);
+          joinJunction(jTest);
+          setJunction(jBody);
+          joinJunction(jExit);
+          break;
+        case 'label':
+          assert(node[2][0] in BREAK_CAPTURERS, 'label on non-loop, non-switch statement')
+          nextLoopLabel = node[1];
+          buildFlowGraph(node[2]);
+          break;
+        case 'switch':
+          // Emscripten generates switch statements of a very limited
+          // form: all case clauses are numeric literals, and all
+          // case bodies end with a (maybe implicit) break.  So it's
+          // basically equivalent to a multi-way 'if' statement.
+          isInExpr++;
+          buildFlowGraph(node[1]);
+          isInExpr--;
+          var condition = lookThroughCasts(node[1]);
+          var jCheckExit = markJunction();
+          var jExit = addJunction();
+          pushActiveLabels(null, jExit);
+          var hasDefault = false;
+          for (var i=0; i<node[2].length; i++) {
+            setJunction(jCheckExit);
+            // All case clauses are either 'default' or a numeric literal.
+            if (!node[2][i][0]) {
+              hasDefault = true;
+            } else {
+              // Detect switches dispatching to labelled blocks.
+              if (condition[0] === 'name' && condition[1] === 'label') {
+                addBlockLabel(lookThroughCasts(node[2][i][0]));
+              }
+            }
+            for (var j = 0; j < node[2][i][1].length; j++) {
+              buildFlowGraph(node[2][i][1][j]);
+            }
+            // Control flow will never actually reach the end of the case body.
+            // If there's live code here, assume it jumps to case exit.
+            if (currEntryJunction !== null && nextBasicBlock.nodes.length > 0) {
+              if (node[2][i][0]) {
+                markNonLocalJump('return');
+              } else {
+                joinJunction(jExit);
+              }
+            }
+          }
+          // If there was no default case, we also need an empty block
+          // linking straight from the test evaluation to the exit.
+          if (!hasDefault) {
+            setJunction(jCheckExit);
+          }
+          joinJunction(jExit);
+          popActiveLabels()
+          break;
+        case 'return':
+          if (node[1]) {
+            isInExpr++;
+            buildFlowGraph(node[1]);
+            isInExpr--;
+          }
+          markNonLocalJump(type);
+          break;
+        case 'break':
+        case 'continue':
+          markNonLocalJump(type, node[1]);
+          break;
+        case 'assign':
+          isInExpr++;
+          buildFlowGraph(node[3]);
+          isInExpr--;
+          if (node[1] === true && node[2][0] === 'name') {
+            addKillNode(node);
+          } else {
+            buildFlowGraph(node[2]);
+          }
+          break;
+        case 'name':
+          addUseNode(node);
+          break;
+        case 'block':
+        case 'toplevel':
+          if (node[1]) {
+            for (var i = 0; i < node[1].length; i++) {
+              buildFlowGraph(node[1][i]);
+            }
+          }
+          break;
+        case 'stat':
+          buildFlowGraph(node[1]);
+          break;
+        case 'unary-prefix':
+        case 'unary-postfix':
+          isInExpr++;
+          buildFlowGraph(node[2]);
+          isInExpr--;
+          break;
+        case 'binary':
+          isInExpr++;
+          buildFlowGraph(node[2]);
+          buildFlowGraph(node[3]);
+          isInExpr--;
+          break;
+        case 'call':
+          isInExpr++;
+          buildFlowGraph(node[1]);
+          if (node[2]) {
+            for (var i = 0; i < node[2].length; i++) {
+              buildFlowGraph(node[2][i]);
+            }
+          }
+          isInExpr--;
+          // If the call is statically known to throw,
+          // treat it as a jump to function exit.
+          if (!isInExpr && node[1][0] === 'name') {
+            if (node[1][1] in FUNCTIONS_THAT_ALWAYS_THROW) {
+              markNonLocalJump('return');
+            }
+          }
+          break;
+        case 'seq':
+        case 'sub':
+          isInExpr++;
+          buildFlowGraph(node[1]);
+          buildFlowGraph(node[2]);
+          isInExpr--;
+          break;
+        case 'dot':
+        case 'throw':
+          isInExpr++;
+          buildFlowGraph(node[1]);
+          isInExpr--;
+          break;
+        case 'num':
+        case 'string':
+        case 'var':
+          break;
+        default:
+          printErr(JSON.stringify(node));
+          assert(false, 'unsupported node type: ' + type);
+      }
+    }
+    buildFlowGraph(fun);
+
+    assert(setSize(junctions[ENTRY_JUNCTION].inblocks) === 0, 'function entry must have no incoming blocks');
+    assert(setSize(junctions[EXIT_JUNCTION].outblocks) === 0, 'function exit must have no outgoing blocks');
+    assert(blocks[ENTRY_BLOCK].entry === ENTRY_JUNCTION, 'block zero must be the initial block');
+
+    // Fix up implicit jumps done by assigning to the 'label' variable.
+    // If a block ends with an assignment to 'label' and there's another block
+    // with that value of 'label' as precondition, we tweak the flow graph so
+    // that the former jumps straight to the later.
+
+    var labelledBlocks = {};
+    var labelledJumps = [];
+    FINDLABELLEDBLOCKS:
+    for (var i = 0; i < blocks.length; i++) {
+      var block = blocks[i];
+      // Does it have any labels as preconditions to its entry?
+      for (var labelVal in block.labels) {
+        // If there are multiple blocks with the same label, all bets are off.
+        // This seems to happen sometimes for short blocks that end with a return.
+        // TODO: it should be safe to merge the duplicates if they're identical.
+        if (labelVal in labelledBlocks) {
+          labelledBlocks = {};
+          labelledJumps = [];
+          break FINDLABELLEDBLOCKS;
+        }
+        labelledBlocks[labelVal] = block;
+      }
+      // Does it assign a specific label value at exit?
+      if ('label' in block.kill) {
+        var finalNode = block.nodes[block.nodes.length - 1];
+        if (finalNode[0] === 'assign' && finalNode[2][1] === 'label') {
+          // If labels are computed dynamically then all bets are off.
+          // This can happen due to indirect branching in llvm output.
+          if (finalNode[3][0] !== 'num') {
+            labelledBlocks = {};
+            labelledJumps = [];
+            break FINDLABELLEDBLOCKS;
+          }
+          labelledJumps.push([finalNode[3][1], block]);
+        } else { 
+          // If label is assigned a non-zero value elsewhere in the block
+          // then all bets are off.  This can happen e.g. due to outlining
+          // saving/restoring label to the stack.
+          for (var j = 0; j < block.nodes.length - 1; j++) {
+            if (block.nodes[j][0] === 'assign' && block.nodes[j][2][1] === 'label') {
+              if (block.nodes[j][3][0] !== 'num' && block.nodes[j][3][1] !== 0) {
+                labelledBlocks = {};
+                labelledJumps = [];
+                break FINDLABELLEDBLOCKS;
+              }
+            }
+          }
+        }
+      }
+    }
+    for (var labelVal in labelledBlocks) {
+      var block = labelledBlocks[labelVal];
+      // Disconnect it from the graph, and create a
+      // new junction for jumps targetting this label.
+      delete junctions[block.entry].outblocks[block.id];
+      block.entry = addJunction();
+      junctions[block.entry].outblocks[block.id] = 1;
+      // Add a fake use of 'label' to keep it alive in predecessor.
+      block.use['label'] = 1;
+      block.nodes.unshift(['name', 'label']);
+      block.isexpr.unshift(1);
+    }
+    for (var i = 0; i < labelledJumps.length; i++) {
+      var labelVal = labelledJumps[i][0];
+      var block = labelledJumps[i][1];
+      var targetBlock = labelledBlocks[labelVal];
+      if (targetBlock) {
+        // Redirect its exit to entry of the target block.
+        delete junctions[block.exit].inblocks[block.id];
+        block.exit = targetBlock.entry;
+        junctions[block.exit].inblocks[block.id] = 1;
+      }
+    }
+    labelledBlocks = null;
+    labelledJumps = null;
+
+    // Do a backwards data-flow analysis to determine the set of live
+    // variables at each junction, and to use this information to eliminate
+    // any unused assignments.
+    // We run two nested phases.  The inner phase builds the live set for each
+    // junction.  The outer phase uses this to try to eliminate redundant
+    // stores in each basic block, which might in turn affect liveness info.
+
+    function analyzeJunction(junc) {
+      // Update the live set for this junction.
+      var live = {};
+      for (var b in junc.outblocks) {
+        var block = blocks[b];
+        var liveSucc = junctions[block.exit].live || {};
+        for (var name in liveSucc) {
+          if (!(name in block.kill)) {
+            live[name] = 1;
+          }
+        }
+        for (var name in block.use) {
+          live[name] = 1;
+        }
+      }
+      junc.live = live;
+    }
+
+    function analyzeBlock(block) {
+      // Update information about the behaviour of the block.
+      // This includes the standard 'use' and 'kill' information,
+      // plus a 'link' set naming values that flow through from entry
+      // to exit, possibly changing names via simple 'x=y' assignments.
+      // As we go, we eliminate assignments if the variable is not
+      // subsequently used.
+      var live = copy(junctions[block.exit].live);
+      var use = {};
+      var kill = {};
+      var link = {};
+      var lastUseLoc = {};
+      var firstDeadLoc = {};
+      var firstKillLoc = {};
+      var lastKillLoc = {};
+      for (var name in live) {
+        link[name] = name;
+        lastUseLoc[name] = block.nodes.length;
+        firstDeadLoc[name] = block.nodes.length;
+      }
+      for (var j = block.nodes.length - 1; j >=0 ; j--) {
+        var node = block.nodes[j];
+        if (node[0] === 'name') {
+          var name = node[1];
+          live[name] = 1;
+          use[name] = j;
+          if (lastUseLoc[name] === undefined) {
+            lastUseLoc[name] = j;
+            firstDeadLoc[name] = j;
+          }
+        } else {
+          var name = node[2][1];
+          // We only keep assignments if they will be subsequently used.
+          if (name in live) {
+            kill[name] = 1;
+            delete use[name];
+            delete live[name];
+            firstDeadLoc[name] = j;
+            firstKillLoc[name] = j;
+            if (lastUseLoc[name] === undefined) {
+              lastUseLoc[name] = j;
+            }
+            if (lastKillLoc[name] === undefined) {
+              lastKillLoc[name] = j;
+            }
+            // If it's an "x=y" and "y" is not live, then we can create a
+            // flow-through link from "y" to "x".  If not then there's no
+            // flow-through link for "x".
+            var oldLink = link[name];
+            if (oldLink) {
+              delete link[name];
+              if (node[3][0] === 'name') {
+                if (node[3][1] in localVars) {
+                  link[node[3][1]] = oldLink;
+                }
+              }
+            }
+          } else {
+            // The result of this assignment is never used, so delete it.
+            // We may need to keep the RHS for its value or its side-effects.
+            function removeUnusedNodes(j, n) {
+              for (var name in lastUseLoc) {
+                lastUseLoc[name] -= n;
+              }
+              for (var name in firstKillLoc) {
+                firstKillLoc[name] -= n;
+              }
+              for (var name in lastKillLoc) {
+                lastKillLoc[name] -= n;
+              }
+              for (var name in firstDeadLoc) {
+                firstDeadLoc[name] -= n;
+              }
+              block.nodes.splice(j, n);
+              block.isexpr.splice(j, n);
+            }
+            if (block.isexpr[j] || hasSideEffects(node[3])) {
+              morphNode(node, node[3]);
+              removeUnusedNodes(j, 1);
+            } else {
+              var numUsesInExpr = 0;
+              traverse(node[3], function(node, type) {
+                if (type === 'name' && node[1] in localVars) {
+                  numUsesInExpr++;
+                }
+              });
+              morphNode(node, ['block', []]);
+              j = j - numUsesInExpr;
+              removeUnusedNodes(j, 1 + numUsesInExpr);
+            }
+          }
+        }
+      }
+      block.use = use;
+      block.kill = kill;
+      block.link = link;
+      block.lastUseLoc = lastUseLoc;
+      block.firstDeadLoc = firstDeadLoc;
+      block.firstKillLoc = firstKillLoc;
+      block.lastKillLoc = lastKillLoc;
+    }
+
+    var jWorklistMap = { EXIT_JUNCTION: 1 };
+    var jWorklist = [EXIT_JUNCTION];
+    var bWorklistMap = {};
+    var bWorklist = [];
+
+    // Be sure to visit every junction at least once.
+    // This avoids missing some vars because we disconnected them
+    // when processing the labelled jumps.
+    for (var i = junctions.length - 1; i >= EXIT_JUNCTION; i--) {
+      jWorklistMap[i] = 1;
+      jWorklist.push(i);
+    }
+
+    while (jWorklist.length > 0) {
+      // Iterate on just the junctions until we get stable live sets.
+      // The first run of this loop will grow the live sets to their maximal size.
+      // Subsequent runs will shrink them based on eliminated in-block uses.
+      while (jWorklist.length > 0) {
+        var junc = junctions[jWorklist.pop()];
+        delete jWorklistMap[junc.id];
+        var oldLive = junc.live || null;
+        analyzeJunction(junc);
+        if (!sortedJsonCompare(oldLive, junc.live)) {
+          // Live set changed, updated predecessor blocks and junctions.
+          for (var b in junc.inblocks) {
+            if (!(b in bWorklistMap)) {
+              bWorklistMap[b] = 1;
+              bWorklist.push(b);
+            }
+            var jPred = blocks[b].entry;
+            if (!(jPred in jWorklistMap)) {
+              jWorklistMap[jPred] = 1;
+              jWorklist.push(jPred);
+            }
+          }
+        }
+      }
+      // Now update the blocks based on the calculated live sets.
+      while (bWorklist.length > 0) {
+        var block = blocks[bWorklist.pop()];
+        delete bWorklistMap[block.id];
+        var oldUse = block.use;
+        analyzeBlock(block);
+        if (!sortedJsonCompare(oldUse, block.use)) {
+          // The use set changed, re-process the entry junction.
+          if (!(block.entry in jWorklistMap)) {
+            jWorklistMap[block.entry] = 1;
+            jWorklist.push(block.entry);
+          }
+        }
+      }
+    }
+
+    // Insist that all function parameters are alive at function entry.
+    // This ensures they will be assigned independent registers, even
+    // if they happen to be unused.
+
+    for (var name in asmData.params) {
+      junctions[ENTRY_JUNCTION].live[name] = 1;
+    }
+
+    // For variables that are live at one or more junctions, we assign them
+    // a consistent register for the entire scope of the function.  Find pairs
+    // of variable that cannot use the same register (the "conflicts") as well
+    // as pairs of variables that we'd like to have share the same register
+    // (the "links").
+
+    var junctionVariables = {};
+
+    function initializeJunctionVariable(name) {
+      junctionVariables[name] = { conf: {}, link: {}, excl: {}, reg: null };
+    }
+
+    for (var i = 0; i < junctions.length; i++) {
+      var junc = junctions[i];
+      for (var name in junc.live) {
+        if (!junctionVariables[name]) initializeJunctionVariable(name);
+        // It conflicts with all other names live at this junction.
+        for (var otherName in junc.live) {
+          if (otherName == name) continue;
+          junctionVariables[name].conf[otherName] = 1;
+        }
+        for (var b in junc.outblocks) {
+          // It conflits with any output vars of successor blocks,
+          // if they're assigned before it goes dead in that block.
+          block = blocks[b];
+          var jSucc = junctions[block.exit];
+          for (var otherName in jSucc.live) {
+            if (junc.live[otherName]) continue;
+            if (block.lastKillLoc[otherName] < block.firstDeadLoc[name]) {
+              if (!junctionVariables[otherName]) initializeJunctionVariable(otherName);
+              junctionVariables[name].conf[otherName] = 1;
+              junctionVariables[otherName].conf[name] = 1;
+            }
+          }
+          // It links with any linkages in the outgoing blocks.
+          var linkName = block.link[name];
+          if (linkName && linkName !== name) {
+            if (!junctionVariables[linkName]) initializeJunctionVariable(linkName);
+            junctionVariables[name].link[linkName] = 1;
+            junctionVariables[linkName].link[name] = 1;
+          }
+        }
+      }
+    }
+
+    // Attempt to sort the junction variables to heuristically reduce conflicts.
+    // Simple starting point: handle the most-conflicted variables first.
+    // This seems to work pretty well.
+
+    var sortedJunctionVariables = keys(junctionVariables);
+    sortedJunctionVariables.sort(function(name1, name2) {
+      var jv1 = junctionVariables[name1];
+      var jv2 = junctionVariables[name2];
+      if (jv1.numConfs === undefined) {
+        jv1.numConfs = setSize(jv1.conf);
+      }
+      if (jv2.numConfs === undefined) {
+        jv2.numConfs = setSize(jv2.conf);
+      }
+      return jv2.numConfs - jv1.numConfs;
+    });
+
+    // We can now assign a register to each junction variable.
+    // Process them in order, trying available registers until we find
+    // one that works, and propagating the choice to linked/conflicted
+    // variables as we go.
+
+    function tryAssignRegister(name, reg) {
+      // Try to assign the given register to the given variable,
+      // and propagate that choice throughout the graph.
+      // Returns true if successful, false if there was a conflict.
+      var jv = junctionVariables[name];
+      if (jv.reg !== null) {
+        return jv.reg === reg;
+      }
+      if (jv.excl[reg]) {
+        return false;
+      }
+      jv.reg = reg;
+      // Exclude use of this register at all conflicting variables.
+      for (var confName in jv.conf) {
+        junctionVariables[confName].excl[reg] = 1;
+      }
+      // Try to propagate it into linked variables.
+      // It's not an error if we can't.
+      for (var linkName in jv.link) {
+        tryAssignRegister(linkName, reg);
+      }
+      return true;
+    }
+
+    NEXTVARIABLE:
+    for (var i = 0; i < sortedJunctionVariables.length; i++) {
+      var name = sortedJunctionVariables[i];
+      // It may already be assigned due to linked-variable propagation.
+      if (junctionVariables[name].reg !== null) {
+        continue NEXTVARIABLE;
+      }
+      // Try to use existing registers first.
+      var allRegs = allRegsByType[localVars[name]];
+      for (var reg in allRegs) {
+        if (tryAssignRegister(name, reg)) {
+          continue NEXTVARIABLE;
+        }
+      }
+      // They're all taken, create a new one.
+      tryAssignRegister(name, createReg(name));
+    }
+
+    // Each basic block can now be processed in turn.
+    // There may be internal-use-only variables that still need a register
+    // assigned, but they can be treated just for this block.  We know
+    // that all inter-block variables are in a good state thanks to
+    // junction variable consistency.
+
+    for (var i = 0; i < blocks.length; i++) {
+      var block = blocks[i];
+      if (block.nodes.length === 0) continue;
+      var jEnter = junctions[block.entry];
+      var jExit = junctions[block.exit];
+      // Mark the point at which each input reg becomes dead.
+      // Variables alive before this point must not be assigned
+      // to that register.
+      var inputVars = {}
+      var inputDeadLoc = {};
+      var inputVarsByReg = {};
+      for (var name in jExit.live) {
+        if (!(name in block.kill)) {
+          inputVars[name] = 1;
+          var reg = junctionVariables[name].reg;
+          assert(reg !== null, 'input variable doesnt have a register');
+          inputDeadLoc[reg] = block.firstDeadLoc[name];
+          inputVarsByReg[reg] = name;
+        }
+      }
+      for (var name in block.use) {
+        if (!(name in inputVars)) {
+          inputVars[name] = 1;
+          var reg = junctionVariables[name].reg;
+          assert(reg !== null, 'input variable doesnt have a register');
+          inputDeadLoc[reg] = block.firstDeadLoc[name];
+          inputVarsByReg[reg] = name;
+        }
+      }
+      assert(setSize(setSub(inputVars, jEnter.live)) == 0);
+      // Scan through backwards, allocating registers on demand.
+      // Be careful to avoid conflicts with the input registers.
+      // We consume free registers in last-used order, which helps to
+      // eliminate "x=y" assignments that are the last use of "y".
+      var assignedRegs = {};
+      var freeRegsByType = copy(allRegsByType);
+      // Begin with all live vars assigned per the exit junction.
+      for (var name in jExit.live) {
+        var reg = junctionVariables[name].reg;
+        assert(reg !== null, 'output variable doesnt have a register');
+        assignedRegs[name] = reg;
+        delete freeRegsByType[localVars[name]][reg];
+      }
+      for (var j = 0; j < freeRegsByType.length; j++) {
+        freeRegsByType[j] = keys(freeRegsByType[j]);
+      }
+      // Scan through the nodes in sequence, modifying each node in-place
+      // and grabbing/freeing registers as needed.
+      var maybeRemoveNodes = [];
+      for (var j = block.nodes.length - 1; j >= 0; j--) {
+        var node = block.nodes[j];
+        var name = node[0] === 'assign' ? node[2][1] : node[1];
+        var allRegs = allRegsByType[localVars[name]];
+        var freeRegs = freeRegsByType[localVars[name]];
+        var reg = assignedRegs[name];
+        if (node[0] === 'name') {
+          // A use.  Grab a register if it doesn't have one.
+          if (!reg) {
+            if (name in inputVars && j <= block.firstDeadLoc[name]) {
+              // Assignment to an input variable, must use pre-assigned reg.
+              reg = junctionVariables[name].reg;
+              assignedRegs[name] = reg;
+              for (var k = freeRegs.length - 1; k >= 0; k--) {
+                if (freeRegs[k] === reg) {
+                  freeRegs.splice(k, 1);
+                  break;
+                }
+              }
+            } else {
+              // Try to use one of the existing free registers.
+              // It must not conflict with an input register.
+              for (var k = freeRegs.length - 1; k >= 0; k--) {
+                reg = freeRegs[k];
+                // Check for conflict with input registers.
+                if (block.firstKillLoc[name] <= inputDeadLoc[reg]) {
+                  if (name !== inputVarsByReg[reg]) {
+                    continue;
+                  }
+                }
+                // Found one!
+                assignedRegs[name] = reg;
+                freeRegs.splice(k, 1);
+                break;
+              }
+              // If we didn't find a suitable register, create a new one.
+              if (!assignedRegs[name]) {
+                reg = createReg(name);
+                assignedRegs[name] = reg;
+              }
+            }
+          }
+          node[1] = allRegs[reg];
+        } else {
+          // A kill. This frees the assigned register.
+          assert(reg, 'live variable doesnt have a reg?')
+          node[2][1] = allRegs[reg];
+          freeRegs.push(reg);
+          delete assignedRegs[name];
+          if (node[3][0] === 'name' && node[3][1] in localVars) {
+            maybeRemoveNodes.push([j, node]);
+          }
+        }
+      }
+      // If we managed to create an "x=x" assignments, remove them.
+      for (var j = 0; j < maybeRemoveNodes.length; j++) {
+        var node = maybeRemoveNodes[j][1];
+        if (node[2][1] === node[3][1]) {
+          if (block.isexpr[maybeRemoveNodes[j][0]]) {
+            morphNode(node, node[2]);
+          } else {
+            morphNode(node, ['block', []]);
+          }
+        }
+      }
+    }
+
+    // Assign registers to function params based on entry junction
+
+    var paramRegs = {}
+    if (fun[2]) {
+      for (var i = 0; i < fun[2].length; i++) {
+        var allRegs = allRegsByType[localVars[fun[2][i]]];
+        fun[2][i] = allRegs[junctionVariables[fun[2][i]].reg];
+        paramRegs[fun[2][i]] = 1;
+      }
+    }
+
+    // That's it!
+    // Re-construct the function with appropriate variable definitions.
+ 
+    var finalAsmData = {
+      params: {},
+      vars: {},
+      inlines: asmData.inlines,
+      ret: asmData.ret,
+    };
+    for (var i = 1; i < nextReg; i++) {
+      var reg;
+      for (var type=0; type<allRegsByType.length; type++) {
+        reg = allRegsByType[type][i];
+        if (reg) break;
+      }
+      if (!paramRegs[reg]) {
+        finalAsmData.vars[reg] = type;
+      } else {
+        finalAsmData.params[reg] = type;
+      }
+    }
+    denormalizeAsm(fun, finalAsmData);
+
+    vacuum(fun);
+
+  });
+}
+*/
+
 // minified names generation
 StringSet RESERVED("do if in for new try var env let");
 const char *VALID_MIN_INITS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$";
@@ -2738,6 +3826,7 @@ int main(int argc, char **argv) {
     else if (str == "optimizeFrounds") optimizeFrounds(doc);
     else if (str == "simplifyIfs") simplifyIfs(doc);
     else if (str == "registerize") registerize(doc);
+    //else if (str == "registerizeHarder") registerizeHarder(doc);
     else if (str == "minifyLocals") minifyLocals(doc);
     else if (str == "minifyWhitespace") {}
     else if (str == "asmLastOpts") asmLastOpts(doc);

From b015fb39dc3fd68c99619564482614ea5deff130 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Wed, 10 Dec 2014 14:22:13 -0800
Subject: [PATCH 07/31] more work on registerizeHarder

---
 tools/optimizer/optimizer.cpp | 274 ++++++++++++++++++----------------
 1 file changed, 149 insertions(+), 125 deletions(-)

diff --git a/tools/optimizer/optimizer.cpp b/tools/optimizer/optimizer.cpp
index 0ed2e13245f68..8961e29097cea 100644
--- a/tools/optimizer/optimizer.cpp
+++ b/tools/optimizer/optimizer.cpp
@@ -2473,7 +2473,7 @@ void registerizeHarder(Ref ast) {
     // Do not try to process non-validating methods, like the heap replacer
     bool abort = false;
     traverse(fun, function(node, type) {
-      if (type === NEW) abort = true;
+      if (type == NEW) abort = true;
     });
     if (abort) return;
 
@@ -2506,145 +2506,169 @@ void registerizeHarder(Ref ast) {
     //    * a single entry junction
     //    * a single exit junction
     //    * a 'use' and 'kill' set of names for the block
-    //    * full sequence of 'name' and 'assign' nodes in the block
+    //    * full sequence of NAME and ASSIGN nodes in the block
     //    * whether each such node appears as part of a larger expression
     //      (and therefore cannot be safely eliminated)
     //    * set of labels that can be used to jump to this block
 
-    var junctions = [];
-    var blocks = [];
-    var currEntryJunction = null;
-    var nextBasicBlock = null;
-    var isInExpr = 0;
-    var activeLabels = [{}];
-    var nextLoopLabel = null;
+    struct Junction {
+      int id;
+      std::unordered_set<int> inblocks, outblocks;
+      Junction(int id_) : id(id_) {}
+    };
+    struct Node {
+    };
+    struct Block {
+      int id, entry, exit;
+      StringSet labels;
+      std::vector<Node> nodes;
+      std::vector<bool> isexpr;
+      StringSet use;
+      StringSet kill;
+      Block() : id(-1), entry(-1), exit(-1) {}
+    };
+    struct ContinueBreak {
+      int co, br;
+      BreakContinue(int co_, int br_) : co(co_), br(br_) {}
+    };
+    typedef std::unordered_map<IString, ContinueBreak> LabelState;
 
-    var ENTRY_JUNCTION = 0;
-    var EXIT_JUNCTION = 1;
-    var ENTRY_BLOCK = 0;
+    std::vector<Junction> junctions;
+    std::vector<Block*> blocks;
+    int currEntryJunction = -1;
+    Block* nextBasicBlock = nullptr;
+    bool isInExpr = 0;
+    std::vector<LabelState> activeLabels;
+    IString nextLoopLabel;
 
-    function addJunction() {
+    const int ENTRY_JUNCTION = 0;
+    const int EXIT_JUNCTION = 1;
+    const int ENTRY_BLOCK = 0;
+
+    auto addJunction = [&]() {
       // Create a new junction, without inserting it into the graph.
       // This is useful for e.g. pre-allocating an exit node.
-      var id = junctions.length;
-      junctions[id] = {id: id, inblocks: {}, outblocks: {}};
+      int id = junctions.size();
+      junctions.push_back(Junction(id));
       return id;
-    }
+    };
 
-    function markJunction(id) {
+    auto markJunction = [&](int id=-1) {
       // Mark current traversal location as a junction.
       // This makes a new basic block exiting at this position.
-      if (id === undefined || id === null) {
+      if (id < 0) {
         id = addJunction();
       }
       joinJunction(id, true);
       return id;
-    }
+    };
 
-    function setJunction(id, force) {
+    auto setJunction = [&](int id, bool force) {
       // Set the next entry junction to the given id.
       // This can be used to enter at a previously-declared point.
       // You can't return to a junction with no incoming blocks
       // unless the 'force' parameter is specified.
-      assert(nextBasicBlock.nodes.length === 0, 'refusing to abandon an in-progress basic block')
-      if (force || setSize(junctions[id].inblocks) > 0) {
+      assert(nextBasicBlock.nodes.size() == 0); // refusing to abandon an in-progress basic block
+      if (force || junctions[id].inblocks.size() > 0) {
         currEntryJunction = id;
       } else {
-        currEntryJunction = null;
+        currEntryJunction = -1;
       }
-    }
+    };
 
-    function joinJunction(id, force) {
+    auto joinJunction = [&](int id, bool force) {
       // Complete the pending basic block by exiting at this position.
       // This can be used to exit at a previously-declared point.
-      if (currEntryJunction !== null) {
-        nextBasicBlock.id = blocks.length;
-        nextBasicBlock.entry = currEntryJunction;
-        nextBasicBlock.exit = id;
-        junctions[currEntryJunction].outblocks[nextBasicBlock.id] = 1;
-        junctions[id].inblocks[nextBasicBlock.id] = 1;
-        blocks.push(nextBasicBlock);
+      if (currEntryJunction >= 0) {
+        assert(nextBasicBlock);
+        nextBasicBlock->id = blocks.size();
+        nextBasicBlock->entry = currEntryJunction;
+        nextBasicBlock->exit = id;
+        junctions[currEntryJunction].outblocks.insert(nextBasicBlock.id);
+        junctions[id].inblocks.insert(nextBasicBlock.id);
+        blocks.push_back(nextBasicBlock);
       } 
-      nextBasicBlock = { id: null, entry: null, exit: null, labels: {}, nodes: [], isexpr: [], use: {}, kill: {} };
+      nextBasicBlock = new Block();
       setJunction(id, force);
       return id;
-    }
+    };
 
-    function pushActiveLabels(onContinue, onBreak) {
+    IString NULL_STR;
+
+    auto pushActiveLabels = [&](int onContinue, int onBreak) {
       // Push the target junctions for continuing/breaking a loop.
       // This should be called before traversing into a loop.
-      var prevLabels = activeLabels[activeLabels.length-1];
-      var newLabels = copy(prevLabels);
-      newLabels[null] = [onContinue, onBreak];
-      if (nextLoopLabel) {
-        newLabels[nextLoopLabel] = [onContinue, onBreak];
-        nextLoopLabel = null;
-      }
-      // An unlabelled 'continue' should jump to innermost loop,
+      LabelState& prevLabels = activeLabels.back();
+      LabelState newLabels = prevLabels;
+      newLabels[NULL_STR] = ContinueBreak(onContinue, onBreak);
+      if (!!nextLoopLabel) {
+        newLabels[nextLoopLabel] = ContinueBreak(onContinue, onBreak);
+        nextLoopLabel = NULL_STR;
+      }
+      // An unlabelled CONTINUE should jump to innermost loop,
       // ignoring any nested 'switch' statements.
-      if (onContinue === null && prevLabels[null]) {
-        newLabels[null][0] = prevLabels[null][0];
+      if (onContinue < 0 && prevLabels.count(NULL_STR) > 0) {
+        newLabels[NULL_STR].co = prevLabels[NULL_STR].co;
       }
-      activeLabels.push(newLabels);
-    }
+      activeLabels.push_back(newLabels);
+    };
 
-    function popActiveLabels() {
+    auto popActiveLabels = [&]() {
       // Pop the target junctions for continuing/breaking a loop.
       // This should be called after traversing into a loop.
-      activeLabels.pop();
-    }
+      activeLabels.pop_back();
+    };
 
-    function markNonLocalJump(type, label) {
-      // Complete a block via  'return', 'break' or 'continue'.
+    auto markNonLocalJump = [&](AsmType type, IString label) {
+      // Complete a block via  RETURN, BREAK or CONTINUE.
       // This joins the targetted junction and then sets the current junction to null.
       // Any code traversed before we get back an existing junction is dead code.
-      if (type === 'return') {
+      if (type == RETURN) {
         joinJunction(EXIT_JUNCTION);
       } else {
-        label = label ? label : null;
-        var targets = activeLabels[activeLabels.length-1][label];
-        assert(targets, 'jump to unknown label');
-        if (type === 'continue') {
-          joinJunction(targets[0]);
-        } else if (type === 'break') {
-          joinJunction(targets[1]);
+        assert(activeLabels.back().count(label) > 0); // 'jump to unknown label');
+        auto targets = activeLabels.back()[label];
+        if (type == CONTINUE) {
+          joinJunction(targets.co);
+        } else if (type == BREAK) {
+          joinJunction(targets.br);
         } else {
-          assert(false, 'unknown jump node type');
+          assert(0); // 'unknown jump node type');
         }
       }
-      currEntryJunction = null;
-    }
+      currEntryJunction = nullptr;
+    };
 
-    function addUseNode(node) {
+    auto addUseNode = [&](Ref node) {
       // Mark a use of the given name node in the current basic block.
-      assert(node[0] === 'name', 'not a use node');
-      var name = node[1];
-      if (name in localVars) {
-        nextBasicBlock.nodes.push(node);
-        nextBasicBlock.isexpr.push(isInExpr);
-        if (!nextBasicBlock.kill[name]) {
-          nextBasicBlock.use[name] = 1;
+      assert(node[0] == NAME); // 'not a use node');
+      Ref name = node[1];
+      if (asmData.isLocal(name)) {
+        nextBasicBlock->nodes.push_back(node);
+        nextBasicBlock->isexpr.push_back(isInExpr);
+        if (nextBasicBlock->kill.count(name) == 0) {
+          nextBasicBlock->use.insert(name);
         }
       }
-    }
+    };
 
-    function addKillNode(node) {
+    auto addKillNode = [&](Ref node) {
       // Mark an assignment to the given name node in the current basic block.
-      assert(node[0] === 'assign', 'not a kill node');
-      assert(node[1] === true, 'not a kill node');
-      assert(node[2][0] === 'name', 'not a kill node');
-      var name = node[2][1];
-      if (name in localVars) {
-        nextBasicBlock.nodes.push(node);
-        nextBasicBlock.isexpr.push(isInExpr);
-        nextBasicBlock.kill[name] = 1;
+      assert(node[0] == ASSIGN); //, 'not a kill node');
+      assert(node[1]->isBool(true)); // 'not a kill node');
+      assert(node[2][0] == NAME); //, 'not a kill node');
+      Ref name = node[2][1];
+      if (asmData.isLocal(name)) {
+        nextBasicBlock->nodes.push_back(node);
+        nextBasicBlock->isexpr.push_back(isInExpr);
+        nextBasicBlock->kill.insert(name);
       }
-    }
+    };
 
     function lookThroughCasts(node) {
       // Look through value-preserving casts, like "x | 0" => "x"
-      if (node[0] === 'binary' && node[1] === '|') {
-        if (node[3][0] === 'num' && node[3][1] === 0) {
+      if (node[0] == 'binary' && node[1] == '|') {
+        if (node[3][0] == 'num' && node[3][1] == 0) {
             return lookThroughCasts(node[2]);
         }
       }
@@ -2652,20 +2676,20 @@ void registerizeHarder(Ref ast) {
     }
 
     function addBlockLabel(node) {
-      assert(nextBasicBlock.nodes.length === 0, 'cant add label to an in-progress basic block')
-      if (node[0] === 'num') {
+      assert(nextBasicBlock.nodes.length == 0, 'cant add label to an in-progress basic block')
+      if (node[0] == 'num') {
         nextBasicBlock.labels[node[1]] = 1;
       }
     }
 
     function isTrueNode(node) {
       // Check if the given node is statically truthy.
-      return (node[0] === 'num' && node[1] != 0);
+      return (node[0] == 'num' && node[1] != 0);
     }
 
     function isFalseNode(node) {
       // Check if the given node is statically falsy.
-      return (node[0] === 'num' && node[1] == 0);
+      return (node[0] == 'num' && node[1] == 0);
     }
 
     function morphNode(node, newNode) {
@@ -2692,7 +2716,7 @@ void registerizeHarder(Ref ast) {
   
       // Any code traversed without an active entry junction must be dead,
       // as the resulting block could never be entered. Let's remove it.
-      if (currEntryJunction === null && junctions.length > 0) {
+      if (currEntryJunction == null && junctions.length > 0) {
         morphNode(node, ['block', []]);
         return;
       }
@@ -2701,9 +2725,9 @@ void registerizeHarder(Ref ast) {
       switch (type) {
         case 'defun':
           var jEntry = markJunction();
-          assert(jEntry === ENTRY_JUNCTION);
+          assert(jEntry == ENTRY_JUNCTION);
           var jExit = addJunction();
-          assert(jExit === EXIT_JUNCTION);
+          assert(jExit == EXIT_JUNCTION);
           for (var i = 0; i < node[3].length; i++) {
             buildFlowGraph(node[3][i]);
           }
@@ -2717,9 +2741,9 @@ void registerizeHarder(Ref ast) {
           var jExit = addJunction();
           if (node[2]) {
             // Detect and mark "if (label == N) { <labelled block> }".
-            if (node[1][0] === 'binary' && node[1][1] === '==') {
+            if (node[1][0] == 'binary' && node[1][1] == '==') {
               var lhs = lookThroughCasts(node[1][2]);
-              if (lhs[0] === 'name' && lhs[1] === 'label') {
+              if (lhs[0] == NAME && lhs[1] == 'label') {
                 addBlockLabel(lookThroughCasts(node[1][3]));
               }
             }
@@ -2868,7 +2892,7 @@ void registerizeHarder(Ref ast) {
               hasDefault = true;
             } else {
               // Detect switches dispatching to labelled blocks.
-              if (condition[0] === 'name' && condition[1] === 'label') {
+              if (condition[0] == NAME && condition[1] == 'label') {
                 addBlockLabel(lookThroughCasts(node[2][i][0]));
               }
             }
@@ -2879,7 +2903,7 @@ void registerizeHarder(Ref ast) {
             // If there's live code here, assume it jumps to case exit.
             if (currEntryJunction !== null && nextBasicBlock.nodes.length > 0) {
               if (node[2][i][0]) {
-                markNonLocalJump('return');
+                markNonLocalJump(RETURN);
               } else {
                 joinJunction(jExit);
               }
@@ -2893,7 +2917,7 @@ void registerizeHarder(Ref ast) {
           joinJunction(jExit);
           popActiveLabels()
           break;
-        case 'return':
+        case RETURN:
           if (node[1]) {
             isInExpr++;
             buildFlowGraph(node[1]);
@@ -2901,21 +2925,21 @@ void registerizeHarder(Ref ast) {
           }
           markNonLocalJump(type);
           break;
-        case 'break':
-        case 'continue':
+        case BREAK:
+        case CONTINUE:
           markNonLocalJump(type, node[1]);
           break;
-        case 'assign':
+        case ASSIGN:
           isInExpr++;
           buildFlowGraph(node[3]);
           isInExpr--;
-          if (node[1] === true && node[2][0] === 'name') {
+          if (node[1] == true && node[2][0] == NAME) {
             addKillNode(node);
           } else {
             buildFlowGraph(node[2]);
           }
           break;
-        case 'name':
+        case NAME:
           addUseNode(node);
           break;
         case 'block':
@@ -2952,9 +2976,9 @@ void registerizeHarder(Ref ast) {
           isInExpr--;
           // If the call is statically known to throw,
           // treat it as a jump to function exit.
-          if (!isInExpr && node[1][0] === 'name') {
+          if (!isInExpr && node[1][0] == NAME) {
             if (node[1][1] in FUNCTIONS_THAT_ALWAYS_THROW) {
-              markNonLocalJump('return');
+              markNonLocalJump(RETURN);
             }
           }
           break;
@@ -2982,9 +3006,9 @@ void registerizeHarder(Ref ast) {
     }
     buildFlowGraph(fun);
 
-    assert(setSize(junctions[ENTRY_JUNCTION].inblocks) === 0, 'function entry must have no incoming blocks');
-    assert(setSize(junctions[EXIT_JUNCTION].outblocks) === 0, 'function exit must have no outgoing blocks');
-    assert(blocks[ENTRY_BLOCK].entry === ENTRY_JUNCTION, 'block zero must be the initial block');
+    assert(setSize(junctions[ENTRY_JUNCTION].inblocks) == 0, 'function entry must have no incoming blocks');
+    assert(setSize(junctions[EXIT_JUNCTION].outblocks) == 0, 'function exit must have no outgoing blocks');
+    assert(blocks[ENTRY_BLOCK].entry == ENTRY_JUNCTION, 'block zero must be the initial block');
 
     // Fix up implicit jumps done by assigning to the 'label' variable.
     // If a block ends with an assignment to 'label' and there's another block
@@ -3011,7 +3035,7 @@ void registerizeHarder(Ref ast) {
       // Does it assign a specific label value at exit?
       if ('label' in block.kill) {
         var finalNode = block.nodes[block.nodes.length - 1];
-        if (finalNode[0] === 'assign' && finalNode[2][1] === 'label') {
+        if (finalNode[0] == ASSIGN && finalNode[2][1] == 'label') {
           // If labels are computed dynamically then all bets are off.
           // This can happen due to indirect branching in llvm output.
           if (finalNode[3][0] !== 'num') {
@@ -3025,7 +3049,7 @@ void registerizeHarder(Ref ast) {
           // then all bets are off.  This can happen e.g. due to outlining
           // saving/restoring label to the stack.
           for (var j = 0; j < block.nodes.length - 1; j++) {
-            if (block.nodes[j][0] === 'assign' && block.nodes[j][2][1] === 'label') {
+            if (block.nodes[j][0] == ASSIGN && block.nodes[j][2][1] == 'label') {
               if (block.nodes[j][3][0] !== 'num' && block.nodes[j][3][1] !== 0) {
                 labelledBlocks = {};
                 labelledJumps = [];
@@ -3045,7 +3069,7 @@ void registerizeHarder(Ref ast) {
       junctions[block.entry].outblocks[block.id] = 1;
       // Add a fake use of 'label' to keep it alive in predecessor.
       block.use['label'] = 1;
-      block.nodes.unshift(['name', 'label']);
+      block.nodes.unshift([NAME, 'label']);
       block.isexpr.unshift(1);
     }
     for (var i = 0; i < labelledJumps.length; i++) {
@@ -3109,11 +3133,11 @@ void registerizeHarder(Ref ast) {
       }
       for (var j = block.nodes.length - 1; j >=0 ; j--) {
         var node = block.nodes[j];
-        if (node[0] === 'name') {
+        if (node[0] == NAME) {
           var name = node[1];
           live[name] = 1;
           use[name] = j;
-          if (lastUseLoc[name] === undefined) {
+          if (lastUseLoc[name] == undefined) {
             lastUseLoc[name] = j;
             firstDeadLoc[name] = j;
           }
@@ -3126,10 +3150,10 @@ void registerizeHarder(Ref ast) {
             delete live[name];
             firstDeadLoc[name] = j;
             firstKillLoc[name] = j;
-            if (lastUseLoc[name] === undefined) {
+            if (lastUseLoc[name] == undefined) {
               lastUseLoc[name] = j;
             }
-            if (lastKillLoc[name] === undefined) {
+            if (lastKillLoc[name] == undefined) {
               lastKillLoc[name] = j;
             }
             // If it's an "x=y" and "y" is not live, then we can create a
@@ -3138,7 +3162,7 @@ void registerizeHarder(Ref ast) {
             var oldLink = link[name];
             if (oldLink) {
               delete link[name];
-              if (node[3][0] === 'name') {
+              if (node[3][0] == NAME) {
                 if (node[3][1] in localVars) {
                   link[node[3][1]] = oldLink;
                 }
@@ -3169,7 +3193,7 @@ void registerizeHarder(Ref ast) {
             } else {
               var numUsesInExpr = 0;
               traverse(node[3], function(node, type) {
-                if (type === 'name' && node[1] in localVars) {
+                if (type == NAME && node[1] in localVars) {
                   numUsesInExpr++;
                 }
               });
@@ -3303,10 +3327,10 @@ void registerizeHarder(Ref ast) {
     sortedJunctionVariables.sort(function(name1, name2) {
       var jv1 = junctionVariables[name1];
       var jv2 = junctionVariables[name2];
-      if (jv1.numConfs === undefined) {
+      if (jv1.numConfs == undefined) {
         jv1.numConfs = setSize(jv1.conf);
       }
-      if (jv2.numConfs === undefined) {
+      if (jv2.numConfs == undefined) {
         jv2.numConfs = setSize(jv2.conf);
       }
       return jv2.numConfs - jv1.numConfs;
@@ -3323,7 +3347,7 @@ void registerizeHarder(Ref ast) {
       // Returns true if successful, false if there was a conflict.
       var jv = junctionVariables[name];
       if (jv.reg !== null) {
-        return jv.reg === reg;
+        return jv.reg == reg;
       }
       if (jv.excl[reg]) {
         return false;
@@ -3367,7 +3391,7 @@ void registerizeHarder(Ref ast) {
 
     for (var i = 0; i < blocks.length; i++) {
       var block = blocks[i];
-      if (block.nodes.length === 0) continue;
+      if (block.nodes.length == 0) continue;
       var jEnter = junctions[block.entry];
       var jExit = junctions[block.exit];
       // Mark the point at which each input reg becomes dead.
@@ -3416,11 +3440,11 @@ void registerizeHarder(Ref ast) {
       var maybeRemoveNodes = [];
       for (var j = block.nodes.length - 1; j >= 0; j--) {
         var node = block.nodes[j];
-        var name = node[0] === 'assign' ? node[2][1] : node[1];
+        var name = node[0] == ASSIGN ? node[2][1] : node[1];
         var allRegs = allRegsByType[localVars[name]];
         var freeRegs = freeRegsByType[localVars[name]];
         var reg = assignedRegs[name];
-        if (node[0] === 'name') {
+        if (node[0] == NAME) {
           // A use.  Grab a register if it doesn't have one.
           if (!reg) {
             if (name in inputVars && j <= block.firstDeadLoc[name]) {
@@ -3428,7 +3452,7 @@ void registerizeHarder(Ref ast) {
               reg = junctionVariables[name].reg;
               assignedRegs[name] = reg;
               for (var k = freeRegs.length - 1; k >= 0; k--) {
-                if (freeRegs[k] === reg) {
+                if (freeRegs[k] == reg) {
                   freeRegs.splice(k, 1);
                   break;
                 }
@@ -3463,7 +3487,7 @@ void registerizeHarder(Ref ast) {
           node[2][1] = allRegs[reg];
           freeRegs.push(reg);
           delete assignedRegs[name];
-          if (node[3][0] === 'name' && node[3][1] in localVars) {
+          if (node[3][0] == NAME && node[3][1] in localVars) {
             maybeRemoveNodes.push([j, node]);
           }
         }
@@ -3471,7 +3495,7 @@ void registerizeHarder(Ref ast) {
       // If we managed to create an "x=x" assignments, remove them.
       for (var j = 0; j < maybeRemoveNodes.length; j++) {
         var node = maybeRemoveNodes[j][1];
-        if (node[2][1] === node[3][1]) {
+        if (node[2][1] == node[3][1]) {
           if (block.isexpr[maybeRemoveNodes[j][0]]) {
             morphNode(node, node[2]);
           } else {
@@ -3519,7 +3543,7 @@ void registerizeHarder(Ref ast) {
 
   });
 }
-*/
+*/ // end registerizeHarder
 
 // minified names generation
 StringSet RESERVED("do if in for new try var env let");

From 9be21543640684d1a9df99bc8ccc17ade623bf42 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Wed, 10 Dec 2014 14:56:08 -0800
Subject: [PATCH 08/31] more registerizeHarder work

---
 tools/optimizer/optimizer.cpp | 576 ++++++++++++++++------------------
 1 file changed, 268 insertions(+), 308 deletions(-)

diff --git a/tools/optimizer/optimizer.cpp b/tools/optimizer/optimizer.cpp
index 8961e29097cea..7669f1d4afd98 100644
--- a/tools/optimizer/optimizer.cpp
+++ b/tools/optimizer/optimizer.cpp
@@ -765,6 +765,8 @@ StringSet ASSOCIATIVE_BINARIES("+ * | & ^"),
           CONDITION_CHECKERS("if do while switch"),
           SAFE_TO_DROP_COERCION("unary-prefix name num");
 
+StringSet BREAK_CAPTURERS("do while for switch"),
+          FUNCTIONS_THAT_ALWAYS_THROW("abort ___resumeException ___cxa_throw ___cxa_rethrow");
 
 bool isFunctionTable(const char *name) {
   static const char *functionTable = "FUNCTION_TABLE";
@@ -1351,7 +1353,7 @@ void eliminate(Ref ast, bool memSafe=false) {
         node[1] = node[1].filter(function(pair) { return !varsToRemove[pair[0]] });
         if (node[1]->size() == 0) {
           // wipe out an empty |var;|
-          node[0] = 'toplevel';
+          node[0] = TOPLEVEL;
           node[1] = [];
         }
       } else */
@@ -2606,7 +2608,7 @@ void registerizeHarder(Ref ast) {
         nextLoopLabel = NULL_STR;
       }
       // An unlabelled CONTINUE should jump to innermost loop,
-      // ignoring any nested 'switch' statements.
+      // ignoring any nested SWITCH statements.
       if (onContinue < 0 && prevLabels.count(NULL_STR) > 0) {
         newLabels[NULL_STR].co = prevLabels[NULL_STR].co;
       }
@@ -2636,7 +2638,7 @@ void registerizeHarder(Ref ast) {
           assert(0); // 'unknown jump node type');
         }
       }
-      currEntryJunction = nullptr;
+      currEntryJunction = -1;
     };
 
     auto addUseNode = [&](Ref node) {
@@ -2665,354 +2667,312 @@ void registerizeHarder(Ref ast) {
       }
     };
 
-    function lookThroughCasts(node) {
+    auto lookThroughCasts = [&](Ref node) {
       // Look through value-preserving casts, like "x | 0" => "x"
-      if (node[0] == 'binary' && node[1] == '|') {
-        if (node[3][0] == 'num' && node[3][1] == 0) {
+      if (node[0] == BINARY && node[1] == OR) {
+        if (node[3][0] == NUM && node[3][1] == 0) {
             return lookThroughCasts(node[2]);
         }
       }
       return node;
-    }
+    };
 
-    function addBlockLabel(node) {
-      assert(nextBasicBlock.nodes.length == 0, 'cant add label to an in-progress basic block')
-      if (node[0] == 'num') {
-        nextBasicBlock.labels[node[1]] = 1;
+    auto addBlockLabel = [&](Ref node) {
+      assert(nextBasicBlock->nodes.size() == 0); // 'cant add label to an in-progress basic block')
+      if (node[0] == NUM) {
+        nextBasicBlock->labels.insert(node[1]); // XXX?
       }
-    }
+    };
 
-    function isTrueNode(node) {
+    auto isTrueNode = [&](Ref node) {
       // Check if the given node is statically truthy.
-      return (node[0] == 'num' && node[1] != 0);
-    }
+      return (node[0] == NUM && node[1]->getNumber() != 0);
+    };
 
-    function isFalseNode(node) {
+    auto isFalseNode = [&](Ref node) {
       // Check if the given node is statically falsy.
-      return (node[0] == 'num' && node[1] == 0);
-    }
-
-    function morphNode(node, newNode) {
-      // In-place morph a node into some other type of node.
-      var i = 0;
-      while (i < node.length && i < newNode.length) {
-        node[i] = newNode[i];
-        i++;
-      }
-      while (i < newNode.length) {
-        node.push(newNode[i]);
-        i++;
-      }
-      if (node.length > newNode.length) {
-        node.length = newNode.length;
-      }
-    }
+      return (node[0] == NUM && node[1]->getNumber() == 0);
+    };
 
-    function buildFlowGraph(node) {
+    std::function<void (Ref)> buildFlowGraph = [&](Ref node) {
       // Recursive function to build up the flow-graph.
       // It walks the tree in execution order, calling the above state-management
       // functions at appropriate points in the traversal.
-      var type = node[0];
+      Ref type = node[0];
   
       // Any code traversed without an active entry junction must be dead,
       // as the resulting block could never be entered. Let's remove it.
-      if (currEntryJunction == null && junctions.length > 0) {
-        morphNode(node, ['block', []]);
+      if (currEntryJunction < 0 && junctions.size() > 0) {
+        safeCopy(node, makeBlock());
         return;
       }
  
       // Traverse each node type according to its particular control-flow semantics.
-      switch (type) {
-        case 'defun':
-          var jEntry = markJunction();
-          assert(jEntry == ENTRY_JUNCTION);
-          var jExit = addJunction();
-          assert(jExit == EXIT_JUNCTION);
-          for (var i = 0; i < node[3].length; i++) {
-            buildFlowGraph(node[3][i]);
+      // TODO: switchify this
+      if (type == DEFUN) {
+        var jEntry = markJunction();
+        assert(jEntry == ENTRY_JUNCTION);
+        int jExit = addJunction();
+        assert(jExit == EXIT_JUNCTION);
+        for (var i = 0; i < node[3].length; i++) {
+          buildFlowGraph(node[3][i]);
+        }
+        joinJunction(jExit);
+      } else if (type == IF) {
+        isInExpr++;
+        buildFlowGraph(node[1]);
+        isInExpr--;
+        int jEnter = markJunction();
+        int jExit = addJunction();
+        if (!!node[2]) {
+          // Detect and mark "if (label == N) { <labelled block> }".
+          if (node[1][0] == BINARY && node[1][1] == EQ) {
+            Ref lhs = lookThroughCasts(node[1][2]);
+            if (lhs[0] == NAME && lhs[1] == LABEL) {
+              addBlockLabel(lookThroughCasts(node[1][3]));
+            }
           }
-          joinJunction(jExit);
-          break;
-        case 'if':
-          isInExpr++;
+          buildFlowGraph(node[2]);
+        }
+        joinJunction(jExit);
+        setJunction(jEnter);
+        if (!!node[3]) {
+          buildFlowGraph(node[3]);
+        }
+        joinJunction(jExit);
+      } else if (type == CONDITIONAL) {
+        isInExpr++;
+        // If the conditional has no side-effects, we can treat it as a single
+        // block, which might open up opportunities to remove it entirely.
+        if (!hasSideEffects(node)) {
           buildFlowGraph(node[1]);
-          isInExpr--;
-          var jEnter = markJunction();
-          var jExit = addJunction();
-          if (node[2]) {
-            // Detect and mark "if (label == N) { <labelled block> }".
-            if (node[1][0] == 'binary' && node[1][1] == '==') {
-              var lhs = lookThroughCasts(node[1][2]);
-              if (lhs[0] == NAME && lhs[1] == 'label') {
-                addBlockLabel(lookThroughCasts(node[1][3]));
-              }
-            }
+          if (!!node[2]) {
+            buildFlowGraph(node[2]);
+          }
+          if (!!node[3]) {
+            buildFlowGraph(node[3]);
+          }
+        } else {
+          buildFlowGraph(node[1]);
+          int jEnter = markJunction();
+          int jExit = addJunction();
+          if (!!node[2]) {
             buildFlowGraph(node[2]);
           }
           joinJunction(jExit);
           setJunction(jEnter);
-          if (node[3]) {
+          if (!!node[3]) {
             buildFlowGraph(node[3]);
           }
           joinJunction(jExit);
-          break;
-        case 'conditional':
+        }
+        isInExpr--;
+      } else if (type == WHILE) {
+        // Special-case "while (1) {}" to use fewer junctions,
+        // since emscripten generates a lot of these.
+        if (isTrueNode(node[1])) {
+          int jLoop = markJunction();
+          int jExit = addJunction();
+          pushActiveLabels(jLoop, jExit);
+          buildFlowGraph(node[2]);
+          popActiveLabels();
+          joinJunction(jLoop);
+          setJunction(jExit);
+        } else {
+          var jCond = markJunction();
+          int jLoop = addJunction();
+          int jExit = addJunction();
           isInExpr++;
-          // If the conditional has no side-effects, we can treat it as a single
-          // block, which might open up opportunities to remove it entirely.
-          if (!hasSideEffects(node)) {
-            buildFlowGraph(node[1]);
-            if (node[2]) {
-              buildFlowGraph(node[2]);
-            }
-            if (node[3]) {
-              buildFlowGraph(node[3]);
-            }
-          } else {
-            buildFlowGraph(node[1]);
-            var jEnter = markJunction();
-            var jExit = addJunction();
-            if (node[2]) {
-              buildFlowGraph(node[2]);
-            }
-            joinJunction(jExit);
-            setJunction(jEnter);
-            if (node[3]) {
-              buildFlowGraph(node[3]);
-            }
-            joinJunction(jExit);
-          }
-          isInExpr--;
-          break;
-        case 'while':
-          // Special-case "while (1) {}" to use fewer junctions,
-          // since emscripten generates a lot of these.
-          if (isTrueNode(node[1])) {
-            var jLoop = markJunction();
-            var jExit = addJunction();
-            pushActiveLabels(jLoop, jExit);
-            buildFlowGraph(node[2]);
-            popActiveLabels();
-            joinJunction(jLoop);
-            setJunction(jExit);
-          } else {
-            var jCond = markJunction();
-            var jLoop = addJunction();
-            var jExit = addJunction();
-            isInExpr++;
-            buildFlowGraph(node[1]);
-            isInExpr--;
-            joinJunction(jLoop);
-            pushActiveLabels(jCond, jExit);
-            buildFlowGraph(node[2]);
-            popActiveLabels();
-            joinJunction(jCond);
-            // An empty basic-block linking condition exit to loop exit.
-            setJunction(jLoop);
-            joinJunction(jExit);
-          }
-          break;
-        case 'do':
-          // Special-case "do {} while (1)" and "do {} while (0)" to use
-          // fewer junctions, since emscripten generates a lot of these.
-          if (isFalseNode(node[1])) {
-            var jExit = addJunction();
-            pushActiveLabels(jExit, jExit);
-            buildFlowGraph(node[2]);
-            popActiveLabels();
-            joinJunction(jExit);
-          } else if (isTrueNode(node[1])) {
-            var jLoop = markJunction();
-            var jExit = addJunction();
-            pushActiveLabels(jLoop, jExit);
-            buildFlowGraph(node[2]);
-            popActiveLabels();
-            joinJunction(jLoop);
-            setJunction(jExit);
-          } else {
-            var jLoop = markJunction();
-            var jCond = addJunction();
-            var jCondExit = addJunction();
-            var jExit = addJunction();
-            pushActiveLabels(jCond, jExit);
-            buildFlowGraph(node[2]);
-            popActiveLabels();
-            joinJunction(jCond);
-            isInExpr++;
-            buildFlowGraph(node[1]);
-            isInExpr--;
-            joinJunction(jCondExit);
-            joinJunction(jLoop);
-            setJunction(jCondExit);
-            joinJunction(jExit)
-          }
-          break;
-        case 'for':
-          var jTest = addJunction();
-          var jBody = addJunction();
-          var jStep = addJunction();
-          var jExit = addJunction();
           buildFlowGraph(node[1]);
-          joinJunction(jTest);
-          isInExpr++;
-          buildFlowGraph(node[2]);
           isInExpr--;
-          joinJunction(jBody);
-          pushActiveLabels(jStep, jExit);
-          buildFlowGraph(node[4]);
+          joinJunction(jLoop);
+          pushActiveLabels(jCond, jExit);
+          buildFlowGraph(node[2]);
           popActiveLabels();
-          joinJunction(jStep);
-          buildFlowGraph(node[3]);
-          joinJunction(jTest);
-          setJunction(jBody);
+          joinJunction(jCond);
+          // An empty basic-block linking condition exit to loop exit.
+          setJunction(jLoop);
           joinJunction(jExit);
-          break;
-        case 'label':
-          assert(node[2][0] in BREAK_CAPTURERS, 'label on non-loop, non-switch statement')
-          nextLoopLabel = node[1];
+        }
+      } else if (type == DO) {
+        // Special-case "do {} while (1)" and "do {} while (0)" to use
+        // fewer junctions, since emscripten generates a lot of these.
+        if (isFalseNode(node[1])) {
+          int jExit = addJunction();
+          pushActiveLabels(jExit, jExit);
           buildFlowGraph(node[2]);
-          break;
-        case 'switch':
-          // Emscripten generates switch statements of a very limited
-          // form: all case clauses are numeric literals, and all
-          // case bodies end with a (maybe implicit) break.  So it's
-          // basically equivalent to a multi-way 'if' statement.
-          isInExpr++;
-          buildFlowGraph(node[1]);
-          isInExpr--;
-          var condition = lookThroughCasts(node[1]);
-          var jCheckExit = markJunction();
-          var jExit = addJunction();
-          pushActiveLabels(null, jExit);
-          var hasDefault = false;
-          for (var i=0; i<node[2].length; i++) {
-            setJunction(jCheckExit);
-            // All case clauses are either 'default' or a numeric literal.
-            if (!node[2][i][0]) {
-              hasDefault = true;
-            } else {
-              // Detect switches dispatching to labelled blocks.
-              if (condition[0] == NAME && condition[1] == 'label') {
-                addBlockLabel(lookThroughCasts(node[2][i][0]));
-              }
-            }
-            for (var j = 0; j < node[2][i][1].length; j++) {
-              buildFlowGraph(node[2][i][1][j]);
-            }
-            // Control flow will never actually reach the end of the case body.
-            // If there's live code here, assume it jumps to case exit.
-            if (currEntryJunction !== null && nextBasicBlock.nodes.length > 0) {
-              if (node[2][i][0]) {
-                markNonLocalJump(RETURN);
-              } else {
-                joinJunction(jExit);
-              }
-            }
-          }
-          // If there was no default case, we also need an empty block
-          // linking straight from the test evaluation to the exit.
-          if (!hasDefault) {
-            setJunction(jCheckExit);
-          }
+          popActiveLabels();
           joinJunction(jExit);
-          popActiveLabels()
-          break;
-        case RETURN:
-          if (node[1]) {
-            isInExpr++;
-            buildFlowGraph(node[1]);
-            isInExpr--;
-          }
-          markNonLocalJump(type);
-          break;
-        case BREAK:
-        case CONTINUE:
-          markNonLocalJump(type, node[1]);
-          break;
-        case ASSIGN:
-          isInExpr++;
-          buildFlowGraph(node[3]);
-          isInExpr--;
-          if (node[1] == true && node[2][0] == NAME) {
-            addKillNode(node);
-          } else {
-            buildFlowGraph(node[2]);
-          }
-          break;
-        case NAME:
-          addUseNode(node);
-          break;
-        case 'block':
-        case 'toplevel':
-          if (node[1]) {
-            for (var i = 0; i < node[1].length; i++) {
-              buildFlowGraph(node[1][i]);
-            }
-          }
-          break;
-        case 'stat':
-          buildFlowGraph(node[1]);
-          break;
-        case 'unary-prefix':
-        case 'unary-postfix':
-          isInExpr++;
+        } else if (isTrueNode(node[1])) {
+          int jLoop = markJunction();
+          int jExit = addJunction();
+          pushActiveLabels(jLoop, jExit);
           buildFlowGraph(node[2]);
-          isInExpr--;
-          break;
-        case 'binary':
-          isInExpr++;
+          popActiveLabels();
+          joinJunction(jLoop);
+          setJunction(jExit);
+        } else {
+          int jLoop = markJunction();
+          int jCond = addJunction();
+          int jCondExit = addJunction();
+          int jExit = addJunction();
+          pushActiveLabels(jCond, jExit);
           buildFlowGraph(node[2]);
-          buildFlowGraph(node[3]);
-          isInExpr--;
-          break;
-        case 'call':
+          popActiveLabels();
+          joinJunction(jCond);
           isInExpr++;
           buildFlowGraph(node[1]);
-          if (node[2]) {
-            for (var i = 0; i < node[2].length; i++) {
-              buildFlowGraph(node[2][i]);
+          isInExpr--;
+          joinJunction(jCondExit);
+          joinJunction(jLoop);
+          setJunction(jCondExit);
+          joinJunction(jExit)
+        }
+      } else if (type == FOR) {
+        int jTest = addJunction();
+        int jBody = addJunction();
+        int jStep = addJunction();
+        int jExit = addJunction();
+        buildFlowGraph(node[1]);
+        joinJunction(jTest);
+        isInExpr++;
+        buildFlowGraph(node[2]);
+        isInExpr--;
+        joinJunction(jBody);
+        pushActiveLabels(jStep, jExit);
+        buildFlowGraph(node[4]);
+        popActiveLabels();
+        joinJunction(jStep);
+        buildFlowGraph(node[3]);
+        joinJunction(jTest);
+        setJunction(jBody);
+        joinJunction(jExit);
+      } else if (type == LABEL) {
+        assert(BREAK_CAPTURERS.has(node[2][0])); // 'label on non-loop, non-switch statement')
+        nextLoopLabel = node[1];
+        buildFlowGraph(node[2]);
+      } else if (type == SWITCH) {
+        // Emscripten generates switch statements of a very limited
+        // form: all case clauses are numeric literals, and all
+        // case bodies end with a (maybe implicit) break.  So it's
+        // basically equivalent to a multi-way IF statement.
+        isInExpr++;
+        buildFlowGraph(node[1]);
+        isInExpr--;
+        Ref condition = lookThroughCasts(node[1]);
+        int jCheckExit = markJunction();
+        int jExit = addJunction();
+        pushActiveLabels(-1, jExit);
+        bool hasDefault = false;
+        for (int i = 0; i < node[2]->size(); i++) {
+          setJunction(jCheckExit);
+          // All case clauses are either 'default' or a numeric literal.
+          if (!node[2][i][0]) {
+            hasDefault = true;
+          } else {
+            // Detect switches dispatching to labelled blocks.
+            if (condition[0] == NAME && condition[1] == LABEL) {
+              addBlockLabel(lookThroughCasts(node[2][i][0]));
             }
           }
-          isInExpr--;
-          // If the call is statically known to throw,
-          // treat it as a jump to function exit.
-          if (!isInExpr && node[1][0] == NAME) {
-            if (node[1][1] in FUNCTIONS_THAT_ALWAYS_THROW) {
+          for (int j = 0; j < node[2][i][1]->size(); j++) {
+            buildFlowGraph(node[2][i][1][j]);
+          }
+          // Control flow will never actually reach the end of the case body.
+          // If there's live code here, assume it jumps to case exit.
+          if (currEntryJunction >= 0 && nextBasicBlock->nodes.size() > 0) {
+            if (!!node[2][i][0]) {
               markNonLocalJump(RETURN);
+            } else {
+              joinJunction(jExit);
             }
           }
-          break;
-        case 'seq':
-        case 'sub':
-          isInExpr++;
-          buildFlowGraph(node[1]);
-          buildFlowGraph(node[2]);
-          isInExpr--;
-          break;
-        case 'dot':
-        case 'throw':
+        }
+        // If there was no default case, we also need an empty block
+        // linking straight from the test evaluation to the exit.
+        if (!hasDefault) {
+          setJunction(jCheckExit);
+        }
+        joinJunction(jExit);
+        popActiveLabels()
+      } else if (type == RETURN) {
+        if (!!node[1]) {
           isInExpr++;
           buildFlowGraph(node[1]);
           isInExpr--;
-          break;
-        case 'num':
-        case 'string':
-        case 'var':
-          break;
-        default:
-          printErr(JSON.stringify(node));
-          assert(false, 'unsupported node type: ' + type);
+        }
+        markNonLocalJump(type);
+      } else if (type == BREAK || type == CONTINUE) {
+        markNonLocalJump(type, node[1]);
+      } else if (type == ASSIGN) {
+        isInExpr++;
+        buildFlowGraph(node[3]);
+        isInExpr--;
+        if (node[1]->isBool(true) && node[2][0] == NAME) {
+          addKillNode(node);
+        } else {
+          buildFlowGraph(node[2]);
+        }
+      } else if (type == NAME) {
+        addUseNode(node);
+      } else if (type == BLOCK || type == TOPLEVEL) {
+        if (!!node[1]) {
+          for (int i = 0; i < node[1]->size(); i++) {
+            buildFlowGraph(node[1][i]);
+          }
+        }
+      } else if (type == STAT) {
+        buildFlowGraph(node[1]);
+      } else if (type == UNARY_PREFIX || type == UNARY_POSTFIX) {
+        isInExpr++;
+        buildFlowGraph(node[2]);
+        isInExpr--;
+      } else if (type == BINARY) {
+        isInExpr++;
+        buildFlowGraph(node[2]);
+        buildFlowGraph(node[3]);
+        isInExpr--;
+      } else if (type == CALL) {
+        isInExpr++;
+        buildFlowGraph(node[1]);
+        if (!!node[2]) {
+          for (int i = 0; i < node[2]->size(); i++) {
+            buildFlowGraph(node[2][i]);
+          }
+        }
+        isInExpr--;
+        // If the call is statically known to throw,
+        // treat it as a jump to function exit.
+        if (!isInExpr && node[1][0] == NAME) {
+          if (FUNCTIONS_THAT_ALWAYS_THROW.has(node[1][1])) {
+            markNonLocalJump(RETURN);
+          }
+        }
+      } else if (type == SEQ || type == SUB) {:
+        isInExpr++;
+        buildFlowGraph(node[1]);
+        buildFlowGraph(node[2]);
+        isInExpr--;
+      } else if (type == DOT || type == THROW) {
+        isInExpr++;
+        buildFlowGraph(node[1]);
+        isInExpr--;
+      } else if (type == NUM || type == STRING || type == VAR) {
+        // nada
+      } else {
+        assert(0); // 'unsupported node type: ' + type);
       }
-    }
+    };
+
     buildFlowGraph(fun);
 
-    assert(setSize(junctions[ENTRY_JUNCTION].inblocks) == 0, 'function entry must have no incoming blocks');
-    assert(setSize(junctions[EXIT_JUNCTION].outblocks) == 0, 'function exit must have no outgoing blocks');
-    assert(blocks[ENTRY_BLOCK].entry == ENTRY_JUNCTION, 'block zero must be the initial block');
+    assert(junctions[ENTRY_JUNCTION].inblocks.size() == 0); // 'function entry must have no incoming blocks');
+    assert(junctions[EXIT_JUNCTION].outblocks.size() == 0); // 'function exit must have no outgoing blocks');
+    assert(blocks[ENTRY_BLOCK].entry == ENTRY_JUNCTION); //, 'block zero must be the initial block');
 
-    // Fix up implicit jumps done by assigning to the 'label' variable.
-    // If a block ends with an assignment to 'label' and there's another block
-    // with that value of 'label' as precondition, we tweak the flow graph so
+    // Fix up implicit jumps done by assigning to the LABEL variable.
+    // If a block ends with an assignment to LABEL and there's another block
+    // with that value of LABEL as precondition, we tweak the flow graph so
     // that the former jumps straight to the later.
 
     var labelledBlocks = {};
@@ -3033,12 +2993,12 @@ void registerizeHarder(Ref ast) {
         labelledBlocks[labelVal] = block;
       }
       // Does it assign a specific label value at exit?
-      if ('label' in block.kill) {
+      if (LABEL in block.kill) {
         var finalNode = block.nodes[block.nodes.length - 1];
-        if (finalNode[0] == ASSIGN && finalNode[2][1] == 'label') {
+        if (finalNode[0] == ASSIGN && finalNode[2][1] == LABEL) {
           // If labels are computed dynamically then all bets are off.
           // This can happen due to indirect branching in llvm output.
-          if (finalNode[3][0] !== 'num') {
+          if (finalNode[3][0] !== NUM) {
             labelledBlocks = {};
             labelledJumps = [];
             break FINDLABELLEDBLOCKS;
@@ -3049,8 +3009,8 @@ void registerizeHarder(Ref ast) {
           // then all bets are off.  This can happen e.g. due to outlining
           // saving/restoring label to the stack.
           for (var j = 0; j < block.nodes.length - 1; j++) {
-            if (block.nodes[j][0] == ASSIGN && block.nodes[j][2][1] == 'label') {
-              if (block.nodes[j][3][0] !== 'num' && block.nodes[j][3][1] !== 0) {
+            if (block.nodes[j][0] == ASSIGN && block.nodes[j][2][1] == LABEL) {
+              if (block.nodes[j][3][0] !== NUM && block.nodes[j][3][1] !== 0) {
                 labelledBlocks = {};
                 labelledJumps = [];
                 break FINDLABELLEDBLOCKS;
@@ -3067,9 +3027,9 @@ void registerizeHarder(Ref ast) {
       delete junctions[block.entry].outblocks[block.id];
       block.entry = addJunction();
       junctions[block.entry].outblocks[block.id] = 1;
-      // Add a fake use of 'label' to keep it alive in predecessor.
-      block.use['label'] = 1;
-      block.nodes.unshift([NAME, 'label']);
+      // Add a fake use of LABEL to keep it alive in predecessor.
+      block.use[LABEL] = 1;
+      block.nodes.unshift([NAME, LABEL]);
       block.isexpr.unshift(1);
     }
     for (var i = 0; i < labelledJumps.length; i++) {
@@ -3197,7 +3157,7 @@ void registerizeHarder(Ref ast) {
                   numUsesInExpr++;
                 }
               });
-              morphNode(node, ['block', []]);
+              morphNode(node, [BLOCK, []]);
               j = j - numUsesInExpr;
               removeUnusedNodes(j, 1 + numUsesInExpr);
             }
@@ -3393,7 +3353,7 @@ void registerizeHarder(Ref ast) {
       var block = blocks[i];
       if (block.nodes.length == 0) continue;
       var jEnter = junctions[block.entry];
-      var jExit = junctions[block.exit];
+      int jExit = junctions[block.exit];
       // Mark the point at which each input reg becomes dead.
       // Variables alive before this point must not be assigned
       // to that register.
@@ -3499,7 +3459,7 @@ void registerizeHarder(Ref ast) {
           if (block.isexpr[maybeRemoveNodes[j][0]]) {
             morphNode(node, node[2]);
           } else {
-            morphNode(node, ['block', []]);
+            morphNode(node, [BLOCK, []]);
           }
         }
       }

From c2226b1afc1d758ad98fd40e54339dd18e3aee77 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Wed, 10 Dec 2014 15:22:28 -0800
Subject: [PATCH 09/31] fix glfwEnable|Disable, fixes #3059

---
 src/library_glfw.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/library_glfw.js b/src/library_glfw.js
index 7998022485c9b..408b3031b88ec 100644
--- a/src/library_glfw.js
+++ b/src/library_glfw.js
@@ -1188,12 +1188,12 @@ var LibraryGLFW = {
     _sleep(time);
   },
 
-  glfwEnable: function(token) {
+  glfwEnable: function(target) {
     target = GLFW.GLFW2ParamToGLFW3Param(target);
     GLFW.hints[target] = false;
   },
 
-  glfwDisable: function(token) {
+  glfwDisable: function(target) {
     target = GLFW.GLFW2ParamToGLFW3Param(target);
     GLFW.hints[target] = true;
   },

From d1b2e1ea4f01a29101f7e88c4fed85cdfefacea5 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Wed, 10 Dec 2014 15:26:15 -0800
Subject: [PATCH 10/31] add test for #3059

---
 tests/glfw.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tests/glfw.c b/tests/glfw.c
index 0a7a358b28fe8..2a247603ae573 100644
--- a/tests/glfw.c
+++ b/tests/glfw.c
@@ -49,6 +49,8 @@ void Init()
   if (glfwInit() != GL_TRUE)
     Shut_Down(1);
 
+  glfwEnable(GLFW_KEY_REPEAT); // test for issue #3059
+
   int red_bits = glfwGetWindowParam(GLFW_RED_BITS);
   glfwOpenWindowHint(GLFW_RED_BITS, 8);
   assert(glfwGetWindowParam(GLFW_RED_BITS) == 8);

From 041f6b67516974129af41805021803e9dc542b66 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Wed, 10 Dec 2014 15:50:28 -0800
Subject: [PATCH 11/31] continue on registerizeHarder

---
 tools/optimizer/optimizer.cpp | 108 +++++++++++++++++-----------------
 1 file changed, 54 insertions(+), 54 deletions(-)

diff --git a/tools/optimizer/optimizer.cpp b/tools/optimizer/optimizer.cpp
index 7669f1d4afd98..2b244e97b8c55 100644
--- a/tools/optimizer/optimizer.cpp
+++ b/tools/optimizer/optimizer.cpp
@@ -2516,6 +2516,7 @@ void registerizeHarder(Ref ast) {
     struct Junction {
       int id;
       std::unordered_set<int> inblocks, outblocks;
+      StringSet live;
       Junction(int id_) : id(id_) {}
     };
     struct Node {
@@ -2975,44 +2976,45 @@ void registerizeHarder(Ref ast) {
     // with that value of LABEL as precondition, we tweak the flow graph so
     // that the former jumps straight to the later.
 
-    var labelledBlocks = {};
-    var labelledJumps = [];
+    std::unordered_map<IString, Block*> labelledBlocks;
+    typedef std::pair(Ref, Block*) Jump;
+    std::vector<Jump> labelledJumps;
     FINDLABELLEDBLOCKS:
-    for (var i = 0; i < blocks.length; i++) {
-      var block = blocks[i];
+    for (int i = 0; i < blocks.size(); i++) {
+      Block* block = blocks[i];
       // Does it have any labels as preconditions to its entry?
-      for (var labelVal in block.labels) {
+      for (auto labelVal : block->labels) {
         // If there are multiple blocks with the same label, all bets are off.
         // This seems to happen sometimes for short blocks that end with a return.
         // TODO: it should be safe to merge the duplicates if they're identical.
-        if (labelVal in labelledBlocks) {
-          labelledBlocks = {};
-          labelledJumps = [];
+        if (labelledBlocks.count(labelVal) > 0) {
+          labelledBlocks.clear();
+          labelledJumps.clear();
           break FINDLABELLEDBLOCKS;
         }
         labelledBlocks[labelVal] = block;
       }
       // Does it assign a specific label value at exit?
-      if (LABEL in block.kill) {
-        var finalNode = block.nodes[block.nodes.length - 1];
+      if (block.kill.has(LABEL)) {
+        var finalNode = block->nodes.back();
         if (finalNode[0] == ASSIGN && finalNode[2][1] == LABEL) {
           // If labels are computed dynamically then all bets are off.
           // This can happen due to indirect branching in llvm output.
-          if (finalNode[3][0] !== NUM) {
-            labelledBlocks = {};
-            labelledJumps = [];
+          if (finalNode[3][0] != NUM) {
+            labelledBlocks.clear();
+            labelledJumps.clear();
             break FINDLABELLEDBLOCKS;
           }
-          labelledJumps.push([finalNode[3][1], block]);
+          labelledJumps.push(Jump(finalNode[3][1], block));
         } else { 
           // If label is assigned a non-zero value elsewhere in the block
           // then all bets are off.  This can happen e.g. due to outlining
           // saving/restoring label to the stack.
-          for (var j = 0; j < block.nodes.length - 1; j++) {
+          for (int j = 0; j < block->nodes.size() - 1; j++) {
             if (block.nodes[j][0] == ASSIGN && block.nodes[j][2][1] == LABEL) {
-              if (block.nodes[j][3][0] !== NUM && block.nodes[j][3][1] !== 0) {
-                labelledBlocks = {};
-                labelledJumps = [];
+              if (block.nodes[j][3][0] != NUM && block.nodes[j][3][1]->getNumber() != 0) {
+                labelledBlocks.clear();
+                labelledJumps.clear();
                 break FINDLABELLEDBLOCKS;
               }
             }
@@ -3020,31 +3022,29 @@ void registerizeHarder(Ref ast) {
         }
       }
     }
-    for (var labelVal in labelledBlocks) {
-      var block = labelledBlocks[labelVal];
+    for (auto labelVal : labelledBlocks) {
+      var block = labelVal.second;
       // Disconnect it from the graph, and create a
       // new junction for jumps targetting this label.
-      delete junctions[block.entry].outblocks[block.id];
+      junctions[block.entry].outblocks.erase(block.id);
       block.entry = addJunction();
-      junctions[block.entry].outblocks[block.id] = 1;
+      junctions[block.entry].outblocks.insert(block.id);
       // Add a fake use of LABEL to keep it alive in predecessor.
-      block.use[LABEL] = 1;
-      block.nodes.unshift([NAME, LABEL]);
-      block.isexpr.unshift(1);
-    }
-    for (var i = 0; i < labelledJumps.length; i++) {
-      var labelVal = labelledJumps[i][0];
-      var block = labelledJumps[i][1];
-      var targetBlock = labelledBlocks[labelVal];
+      block.use.insert(LABEL);
+      block.nodes.insert(block.nodes.begin(), makeName(LABEL));
+      block.isexpr.insert(block.isexpr(), 1);
+    }
+    for (int i = 0; i < labelledJumps.size(); i++) {
+      auto labelVal = labelledJumps[i].first;
+      auto block = labelledJumps[i].second;
+      Block* targetBlock = labelledBlocks[labelVal];
       if (targetBlock) {
         // Redirect its exit to entry of the target block.
-        delete junctions[block.exit].inblocks[block.id];
-        block.exit = targetBlock.entry;
-        junctions[block.exit].inblocks[block.id] = 1;
+        junctions[block->exit].inblocks.erase(block->id);
+        block->exit = targetBlock->entry;
+        junctions[block->exit].inblocks.inasert(block->id);
       }
     }
-    labelledBlocks = null;
-    labelledJumps = null;
 
     // Do a backwards data-flow analysis to determine the set of live
     // variables at each junction, and to use this information to eliminate
@@ -3053,23 +3053,23 @@ void registerizeHarder(Ref ast) {
     // junction.  The outer phase uses this to try to eliminate redundant
     // stores in each basic block, which might in turn affect liveness info.
 
-    function analyzeJunction(junc) {
+    auto analyzeJunction = [&](Junction& junc) {
       // Update the live set for this junction.
-      var live = {};
-      for (var b in junc.outblocks) {
-        var block = blocks[b];
-        var liveSucc = junctions[block.exit].live || {};
-        for (var name in liveSucc) {
-          if (!(name in block.kill)) {
-            live[name] = 1;
+      StringSet& live = junc.live;
+      std::unordered_set<int> live;
+      for (auto b : junc.outblocks) {
+        Block* block = blocks[b];
+        StringSet& liveSucc = junctions[block->exit].live;
+        for (auto name : liveSucc) {
+          if (!block->kill.has(name)) {
+            live.insert(name);
           }
         }
-        for (var name in block.use) {
-          live[name] = 1;
+        for (auto name : block->use) {
+          live.insert(name);
         }
       }
-      junc.live = live;
-    }
+    };
 
     function analyzeBlock(block) {
       // Update information about the behaviour of the block.
@@ -3270,7 +3270,7 @@ void registerizeHarder(Ref ast) {
           }
           // It links with any linkages in the outgoing blocks.
           var linkName = block.link[name];
-          if (linkName && linkName !== name) {
+          if (linkName && linkName != name) {
             if (!junctionVariables[linkName]) initializeJunctionVariable(linkName);
             junctionVariables[name].link[linkName] = 1;
             junctionVariables[linkName].link[name] = 1;
@@ -3306,7 +3306,7 @@ void registerizeHarder(Ref ast) {
       // and propagate that choice throughout the graph.
       // Returns true if successful, false if there was a conflict.
       var jv = junctionVariables[name];
-      if (jv.reg !== null) {
+      if (jv.reg != null) {
         return jv.reg == reg;
       }
       if (jv.excl[reg]) {
@@ -3329,7 +3329,7 @@ void registerizeHarder(Ref ast) {
     for (var i = 0; i < sortedJunctionVariables.length; i++) {
       var name = sortedJunctionVariables[i];
       // It may already be assigned due to linked-variable propagation.
-      if (junctionVariables[name].reg !== null) {
+      if (junctionVariables[name].reg != null) {
         continue NEXTVARIABLE;
       }
       // Try to use existing registers first.
@@ -3364,7 +3364,7 @@ void registerizeHarder(Ref ast) {
         if (!(name in block.kill)) {
           inputVars[name] = 1;
           var reg = junctionVariables[name].reg;
-          assert(reg !== null, 'input variable doesnt have a register');
+          assert(reg != null, 'input variable doesnt have a register');
           inputDeadLoc[reg] = block.firstDeadLoc[name];
           inputVarsByReg[reg] = name;
         }
@@ -3373,7 +3373,7 @@ void registerizeHarder(Ref ast) {
         if (!(name in inputVars)) {
           inputVars[name] = 1;
           var reg = junctionVariables[name].reg;
-          assert(reg !== null, 'input variable doesnt have a register');
+          assert(reg != null, 'input variable doesnt have a register');
           inputDeadLoc[reg] = block.firstDeadLoc[name];
           inputVarsByReg[reg] = name;
         }
@@ -3388,7 +3388,7 @@ void registerizeHarder(Ref ast) {
       // Begin with all live vars assigned per the exit junction.
       for (var name in jExit.live) {
         var reg = junctionVariables[name].reg;
-        assert(reg !== null, 'output variable doesnt have a register');
+        assert(reg != null, 'output variable doesnt have a register');
         assignedRegs[name] = reg;
         delete freeRegsByType[localVars[name]][reg];
       }
@@ -3424,7 +3424,7 @@ void registerizeHarder(Ref ast) {
                 reg = freeRegs[k];
                 // Check for conflict with input registers.
                 if (block.firstKillLoc[name] <= inputDeadLoc[reg]) {
-                  if (name !== inputVarsByReg[reg]) {
+                  if (name != inputVarsByReg[reg]) {
                     continue;
                   }
                 }

From 22b10d3def1e8b63546c6057caff9830eff95452 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Wed, 10 Dec 2014 17:03:21 -0800
Subject: [PATCH 12/31] handle in html5.h the fact that document, like window,
 does not have getBoundingClientRect; fixes #3060

---
 src/library_html5.js | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/library_html5.js b/src/library_html5.js
index 57f37ae5aca60..b2409328f8ee9 100644
--- a/src/library_html5.js
+++ b/src/library_html5.js
@@ -207,6 +207,10 @@ var LibraryJSEvents = {
       JSEvents.registerOrRemoveHandler(eventHandler);
     },
 
+    getBoundingClientRectOrZeros: function(target) {
+      return target.getBoundingClientRect ? target.getBoundingClientRect() : { left: 0, top: 0 };
+    },
+
     // Copies mouse event data from the given JS mouse event 'e' to the specified Emscripten mouse event structure in the HEAP.
     // eventStruct: the structure to populate.
     // e: The JS mouse event to read data from.
@@ -235,7 +239,7 @@ var LibraryJSEvents = {
         {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.canvasY, '0', 'i32') }}};
       }
       if (target) {
-        var rect = (target === window) ? { left: 0, top: 0 } : target.getBoundingClientRect();
+        var rect = JSEvents.getBoundingClientRectOrZeros(target);
         {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.targetX, 'e.clientX - rect.left', 'i32') }}};
         {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.targetY, 'e.clientY - rect.top', 'i32') }}};        
       } else { // No specific target passed, return 0.
@@ -810,7 +814,7 @@ var LibraryJSEvents = {
         {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchEvent.metaKey, 'e.metaKey', 'i32') }}};
         ptr += {{{ C_STRUCTS.EmscriptenTouchEvent.touches }}}; // Advance to the start of the touch array.
         var canvasRect = Module['canvas'] ? Module['canvas'].getBoundingClientRect() : undefined;
-        var targetRect = (target === window) ? { left: 0, top: 0 } : target.getBoundingClientRect();
+        var targetRect = JSEvents.getBoundingClientRectOrZeros(target);
         var numTouches = 0;
         for(var i in touches) {
           var t = touches[i];

From f9a12e9105f22cf713ac0ba289060037f29e5c00 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Wed, 10 Dec 2014 17:42:42 -0800
Subject: [PATCH 13/31] convert more of registerizeHarder

---
 tools/optimizer/optimizer.cpp | 149 ++++++++++++++++++----------------
 1 file changed, 77 insertions(+), 72 deletions(-)

diff --git a/tools/optimizer/optimizer.cpp b/tools/optimizer/optimizer.cpp
index 2b244e97b8c55..f11a67f004f41 100644
--- a/tools/optimizer/optimizer.cpp
+++ b/tools/optimizer/optimizer.cpp
@@ -3071,59 +3071,59 @@ void registerizeHarder(Ref ast) {
       }
     };
 
-    function analyzeBlock(block) {
+    auto analyzeBlock = [&](Block* block) {
       // Update information about the behaviour of the block.
       // This includes the standard 'use' and 'kill' information,
       // plus a 'link' set naming values that flow through from entry
       // to exit, possibly changing names via simple 'x=y' assignments.
       // As we go, we eliminate assignments if the variable is not
       // subsequently used.
-      var live = copy(junctions[block.exit].live);
-      var use = {};
-      var kill = {};
-      var link = {};
-      var lastUseLoc = {};
-      var firstDeadLoc = {};
-      var firstKillLoc = {};
-      var lastKillLoc = {};
-      for (var name in live) {
+      auto live = junctions[block.exit].live;
+      StringSet use;
+      StringSet kill;
+      StringStringMap link;
+      StringIntMap lastUseLoc;
+      StringIntMap firstDeadLoc;
+      StringIntMap firstKillLoc;
+      StringIntMap lastKillLoc;
+      for (auto name : live) {
         link[name] = name;
-        lastUseLoc[name] = block.nodes.length;
-        firstDeadLoc[name] = block.nodes.length;
+        lastUseLoc[name] = block->nodes.size();
+        firstDeadLoc[name] = block->nodes.size();
       }
-      for (var j = block.nodes.length - 1; j >=0 ; j--) {
-        var node = block.nodes[j];
+      for (int j = block->nodes.size() - 1; j >= 0 ; j--) {
+        Ref node = block->nodes[j];
         if (node[0] == NAME) {
-          var name = node[1];
-          live[name] = 1;
+          IString name = node[1]->getIString();
+          live.insert(name);
           use[name] = j;
-          if (lastUseLoc[name] == undefined) {
+          if (lastUseLoc.count(name) == 0) {
             lastUseLoc[name] = j;
             firstDeadLoc[name] = j;
           }
         } else {
-          var name = node[2][1];
+          IString name = node[2][1]->getIString();
           // We only keep assignments if they will be subsequently used.
-          if (name in live) {
-            kill[name] = 1;
-            delete use[name];
-            delete live[name];
+          if (live.has(name)) {
+            kill.insert(name);
+            use.erase(name);
+            live.erase(name);
             firstDeadLoc[name] = j;
             firstKillLoc[name] = j;
-            if (lastUseLoc[name] == undefined) {
+            if (lastUseLoc.count(name) == 0) {
               lastUseLoc[name] = j;
             }
-            if (lastKillLoc[name] == undefined) {
+            if (lastKillLoc.count(name) == 0) {
               lastKillLoc[name] = j;
             }
             // If it's an "x=y" and "y" is not live, then we can create a
             // flow-through link from "y" to "x".  If not then there's no
             // flow-through link for "x".
-            var oldLink = link[name];
-            if (oldLink) {
-              delete link[name];
+            if (link.has(name)) {
+              IString oldLink = link[name];
+              link.erase(name);
               if (node[3][0] == NAME) {
-                if (node[3][1] in localVars) {
+                if (asmData.isLocal(node[3][1])) {
                   link[node[3][1]] = oldLink;
                 }
               }
@@ -3131,39 +3131,40 @@ void registerizeHarder(Ref ast) {
           } else {
             // The result of this assignment is never used, so delete it.
             // We may need to keep the RHS for its value or its side-effects.
-            function removeUnusedNodes(j, n) {
-              for (var name in lastUseLoc) {
+            auto removeUnusedNodes = [&](int j, int n) {
+              for (auto name : lastUseLoc) {
                 lastUseLoc[name] -= n;
               }
-              for (var name in firstKillLoc) {
+              for (auto name : firstKillLoc) {
                 firstKillLoc[name] -= n;
               }
-              for (var name in lastKillLoc) {
+              for (auto name : lastKillLoc) {
                 lastKillLoc[name] -= n;
               }
-              for (var name in firstDeadLoc) {
+              for (auto name : firstDeadLoc) {
                 firstDeadLoc[name] -= n;
               }
-              block.nodes.splice(j, n);
-              block.isexpr.splice(j, n);
+              block->nodes.erase(block->nodes.begin() + j, block->nodes.begin() + j + n);
+              block->isexpr.erase(block->isexpr.begin() + j, block->isexpr.begin() + j + n);
             }
             if (block.isexpr[j] || hasSideEffects(node[3])) {
-              morphNode(node, node[3]);
+              safeCopy(node, node[3]);
               removeUnusedNodes(j, 1);
             } else {
-              var numUsesInExpr = 0;
-              traverse(node[3], function(node, type) {
-                if (type == NAME && node[1] in localVars) {
+              int numUsesInExpr = 0;
+              traversePre(node[3], [&](Ref node) {
+                if (node[0] == NAME && asmData.isLocal(node[1])) {
                   numUsesInExpr++;
                 }
               });
-              morphNode(node, [BLOCK, []]);
+              morphNode(node, makeBlock());
               j = j - numUsesInExpr;
               removeUnusedNodes(j, 1 + numUsesInExpr);
             }
           }
         }
       }
+      // XXX efficiency
       block.use = use;
       block.kill = kill;
       block.link = link;
@@ -3171,56 +3172,60 @@ void registerizeHarder(Ref ast) {
       block.firstDeadLoc = firstDeadLoc;
       block.firstKillLoc = firstKillLoc;
       block.lastKillLoc = lastKillLoc;
-    }
+    };
 
-    var jWorklistMap = { EXIT_JUNCTION: 1 };
-    var jWorklist = [EXIT_JUNCTION];
-    var bWorklistMap = {};
-    var bWorklist = [];
+    std::unordered_set<int> jWorklistMap;
+    jWorklistMap.insert(EXIT_JUNCTION);
+    std::vector<int> jWorklist;
+    jWorklist.push_back(EXIT_JUNCTION);
+    std::unordered_set<int> bWorklistMap;
+    std::vector<int> bWorklist;
 
     // Be sure to visit every junction at least once.
     // This avoids missing some vars because we disconnected them
     // when processing the labelled jumps.
-    for (var i = junctions.length - 1; i >= EXIT_JUNCTION; i--) {
-      jWorklistMap[i] = 1;
-      jWorklist.push(i);
+    for (int i = junctions.size() - 1; i >= EXIT_JUNCTION; i--) {
+      jWorklistMap.insert(i);
+      jWorklist.push_back(i);
     }
 
-    while (jWorklist.length > 0) {
+    while (jWorklist.size() > 0) {
       // Iterate on just the junctions until we get stable live sets.
       // The first run of this loop will grow the live sets to their maximal size.
       // Subsequent runs will shrink them based on eliminated in-block uses.
-      while (jWorklist.length > 0) {
-        var junc = junctions[jWorklist.pop()];
-        delete jWorklistMap[junc.id];
-        var oldLive = junc.live || null;
+      while (jWorklist.size() > 0) {
+        Junction& junc = junctions[jWorklist.back()];
+        jWorklist.pop_back();
+        jWorklistMap.erase(junc.id);
+        StringSet oldLive = junc.live; // copy it here, to check for changes later
         analyzeJunction(junc);
-        if (!sortedJsonCompare(oldLive, junc.live)) {
+        if (oldLive != junc.live) {
           // Live set changed, updated predecessor blocks and junctions.
-          for (var b in junc.inblocks) {
-            if (!(b in bWorklistMap)) {
-              bWorklistMap[b] = 1;
-              bWorklist.push(b);
+          for (auto b : junc.inblocks) {
+            if (bWorklistMap.count(b) == 0) {
+              bWorklistMap.insert(b)
+              bWorklist.push_back(b);
             }
-            var jPred = blocks[b].entry;
-            if (!(jPred in jWorklistMap)) {
-              jWorklistMap[jPred] = 1;
-              jWorklist.push(jPred);
+            int jPred = blocks[b].entry;
+            if (jWorklistMap.count(jPred) == 0) {
+              jWorklistMap.insert(jPred);
+              jWorklist.push_back(jPred);
             }
           }
         }
       }
       // Now update the blocks based on the calculated live sets.
-      while (bWorklist.length > 0) {
-        var block = blocks[bWorklist.pop()];
-        delete bWorklistMap[block.id];
-        var oldUse = block.use;
+      while (bWorklist.size() > 0) {
+        Block* block = blocks[bWorklist.back()];
+        bWorklist.pop_back();
+        bWorklistMap.erase(block->id);
+        StringSet oldUse = block->use;
         analyzeBlock(block);
-        if (!sortedJsonCompare(oldUse, block.use)) {
+        if (oldUse != block->use) {
           // The use set changed, re-process the entry junction.
-          if (!(block.entry in jWorklistMap)) {
-            jWorklistMap[block.entry] = 1;
-            jWorklist.push(block.entry);
+          if (jWorklistMap.count(block->entry) == 0) {
+            jWorklistMap.insert(block.entry);
+            jWorklist.push_back(block.entry);
           }
         }
       }
@@ -3230,8 +3235,8 @@ void registerizeHarder(Ref ast) {
     // This ensures they will be assigned independent registers, even
     // if they happen to be unused.
 
-    for (var name in asmData.params) {
-      junctions[ENTRY_JUNCTION].live[name] = 1;
+    for (auto name : asmData.params) {
+      junctions[ENTRY_JUNCTION].live.insert(name);
     }
 
     // For variables that are live at one or more junctions, we assign them

From c21b740250345feab44d258cf6e12146706a4c1f Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Wed, 10 Dec 2014 17:57:55 -0800
Subject: [PATCH 14/31] fix default test runner mode, need to force ASM_JS to 2
 now that emcc allows changing it as well

---
 tests/test_core.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/test_core.py b/tests/test_core.py
index 7e297b434094f..8c376f91effc8 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -7232,7 +7232,7 @@ def setUp(self):
   return TT
 
 # Main test modes
-default = make_run("default", compiler=CLANG, emcc_args=[])
+default = make_run("default", compiler=CLANG, emcc_args=["-s", "ASM_JS=2"])
 asm1 = make_run("asm1", compiler=CLANG, emcc_args=["-O1"])
 asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2"])
 asm3 = make_run("asm3", compiler=CLANG, emcc_args=["-O3"])

From ff360a967472b82852d6c1e0fb44d083536e6ed9 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Thu, 11 Dec 2014 11:09:23 -0800
Subject: [PATCH 15/31] return successfully parsed fields if we hit the end in
 the middle of fscanf; fixes #3073

---
 src/library.js     |  2 +-
 tests/test_core.py | 29 +++++++++++++++++++++++++++++
 2 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/src/library.js b/src/library.js
index 432b4880f002b..711cc7296a4a6 100644
--- a/src/library.js
+++ b/src/library.js
@@ -1878,7 +1878,7 @@ LibraryManager.library = {
           }
           unget();
         }
-        if (buffer.length === 0) return 0;  // Failure.
+        if (buffer.length === 0) return fields; // Stop here.
         if (suppressAssignment) continue;
 
         var text = buffer.join('');
diff --git a/tests/test_core.py b/tests/test_core.py
index 8c376f91effc8..e49c6a737b10c 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -4347,6 +4347,35 @@ def test_fscanf(self):
     self.emcc_args += ['--embed-file', 'three_numbers.txt']
     self.do_run(src, 'match = 3\nx = -1.0, y = 0.1, z = -0.1\n')
 
+  def test_fscanf_2(self):
+    if self.emcc_args is None: return self.skip('requires emcc')
+
+    open('a.txt', 'w').write('''1/2/3 4/5/6 7/8/9
+''')
+    self.emcc_args += ['--embed-file', 'a.txt']
+    self.do_run(r'''#include <cstdio>
+#include <iostream>
+
+using namespace std;
+
+int
+main( int argv, char ** argc ) {
+    cout << "fscanf test" << endl;
+
+    FILE * file;
+    file = fopen("a.txt", "rb");
+    int vertexIndex[4];
+    int normalIndex[4];
+    int uvIndex[4];
+
+    int matches = fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex    [1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2], &vertexIndex[3], &uvIndex[3], &normalIndex[3]); 
+
+    cout << matches << endl;
+
+    return 0;
+}
+''', 'fscanf test\n9\n')
+
   def test_fileno(self):
     if self.emcc_args is None: return self.skip('requires emcc')
     open(os.path.join(self.get_dir(), 'empty.txt'), 'w').write('')

From 5b3516518c2b7a526b73b3f39bda75227ba46b1d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= <jujjyl@gmail.com>
Date: Thu, 11 Dec 2014 21:57:15 +0200
Subject: [PATCH 16/31] Fix bad glUniform1i call in tests/float_tex.cpp

---
 tests/float_tex.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/float_tex.cpp b/tests/float_tex.cpp
index c40ff78668dbb..b80457a3ba5bc 100644
--- a/tests/float_tex.cpp
+++ b/tests/float_tex.cpp
@@ -77,7 +77,7 @@ static void glut_draw_callback(void) {
     glActiveTexture(GL_TEXTURE0);
     updateFloatTexture(); //we change the texture each time to create the effect (it is just for the test)
     glBindTexture(GL_TEXTURE_2D, nodeTexture);
-    glUniform1i(nodeSamplerLocation, GL_TEXTURE0);
+    glUniform1i(nodeSamplerLocation, 0);
     glEnableVertexAttribArray(0);
     glBindBuffer(GL_ARRAY_BUFFER, indicesVBO);
     glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, NULL);

From 8c863fe86531f0d8b950ccbb108140db32d62745 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= <jujjyl@gmail.com>
Date: Thu, 11 Dec 2014 22:01:20 +0200
Subject: [PATCH 17/31] Clean up redundant warnings in tests/float_tex.cpp

---
 tests/float_tex.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tests/float_tex.cpp b/tests/float_tex.cpp
index b80457a3ba5bc..f30323894aced 100644
--- a/tests/float_tex.cpp
+++ b/tests/float_tex.cpp
@@ -117,9 +117,11 @@ static void gl_init(void) {
     /* Get the locations of the uniforms so we can access them */
     nodeSamplerLocation      = glGetUniformLocation(program, "nodeInfo");
     glBindAttribLocation(program, 0, "indices");
+#ifndef __EMSCRIPTEN__ // GLES2 & WebGL do not have these, only pre 3.0 desktop GL and compatibility mode GL3.0+ GL do.
     //Enable glPoint size in shader, always enable in Open Gl ES 2.
     glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
     glEnable(GL_POINT_SPRITE);
+#endif
 }
 int main(int argc, char *argv[]) {
     glutInit(&argc, argv);

From 5c888303eb1a7099662c455b77229890969c5bdb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= <jujjyl@gmail.com>
Date: Thu, 11 Dec 2014 22:15:02 +0200
Subject: [PATCH 18/31] Fix tests/float_tex.cpp to use unsized texture internal
 format GL_RGBA instead of sized texture internal format GL_RGBA32F for
 floating point texture, since GLES2/WebGL1 does not support the sized
 variants.

---
 tests/float_tex.cpp | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/tests/float_tex.cpp b/tests/float_tex.cpp
index f30323894aced..698b058e88671 100644
--- a/tests/float_tex.cpp
+++ b/tests/float_tex.cpp
@@ -61,7 +61,13 @@ static void updateFloatTexture() {
         ++count;
     }
     glBindTexture(GL_TEXTURE_2D, nodeTexture);
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, nbNodes, 1, 0, GL_RGBA, GL_FLOAT, data);
+#ifdef __EMSCRIPTEN__ // In GLES2 and WebGL1, we must use unsized texture internal formats.
+    const GLenum internalFormat = GL_RGBA;
+#else
+    // In desktop GL, we can also use sized internal formats.
+    const GLenum internalFormat = GL_RGBA32F;
+#endif
+    glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, nbNodes, 1, 0, GL_RGBA, GL_FLOAT, data);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     glBindTexture(GL_TEXTURE_2D, NULL);

From 11ca950e140e8b8d13c8ff8c0c35101a6e8fd43b Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Thu, 11 Dec 2014 12:16:59 -0800
Subject: [PATCH 19/31] minor fixes to registerizeHarder, and progress on
 conversion to native optimizer

---
 tools/js-optimizer.js         |  4 +-
 tools/optimizer/optimizer.cpp | 81 +++++++++++++++++------------------
 2 files changed, 42 insertions(+), 43 deletions(-)

diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index a7ba7e9f39fe7..462cd717687e1 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -3654,9 +3654,9 @@ function registerizeHarder(ast) {
           junctionVariables[name].conf[otherName] = 1;
         }
         for (var b in junc.outblocks) {
-          // It conflits with any output vars of successor blocks,
+          // It conflicts with any output vars of successor blocks,
           // if they're assigned before it goes dead in that block.
-          block = blocks[b];
+          var block = blocks[b];
           var jSucc = junctions[block.exit];
           for (var otherName in jSucc.live) {
             if (junc.live[otherName]) continue;
diff --git a/tools/optimizer/optimizer.cpp b/tools/optimizer/optimizer.cpp
index f11a67f004f41..e8282027c8aa1 100644
--- a/tools/optimizer/optimizer.cpp
+++ b/tools/optimizer/optimizer.cpp
@@ -2711,11 +2711,11 @@ void registerizeHarder(Ref ast) {
       // Traverse each node type according to its particular control-flow semantics.
       // TODO: switchify this
       if (type == DEFUN) {
-        var jEntry = markJunction();
+        int jEntry = markJunction();
         assert(jEntry == ENTRY_JUNCTION);
         int jExit = addJunction();
         assert(jExit == EXIT_JUNCTION);
-        for (var i = 0; i < node[3].length; i++) {
+        for (int i = 0; i < node[3].length; i++) {
           buildFlowGraph(node[3][i]);
         }
         joinJunction(jExit);
@@ -2780,7 +2780,7 @@ void registerizeHarder(Ref ast) {
           joinJunction(jLoop);
           setJunction(jExit);
         } else {
-          var jCond = markJunction();
+          int jCond = markJunction();
           int jLoop = addJunction();
           int jExit = addJunction();
           isInExpr++;
@@ -2996,7 +2996,7 @@ void registerizeHarder(Ref ast) {
       }
       // Does it assign a specific label value at exit?
       if (block.kill.has(LABEL)) {
-        var finalNode = block->nodes.back();
+        Ref finalNode = block->nodes.back();
         if (finalNode[0] == ASSIGN && finalNode[2][1] == LABEL) {
           // If labels are computed dynamically then all bets are off.
           // This can happen due to indirect branching in llvm output.
@@ -3023,7 +3023,7 @@ void registerizeHarder(Ref ast) {
       }
     }
     for (auto labelVal : labelledBlocks) {
-      var block = labelVal.second;
+      Block* block = labelVal.second;
       // Disconnect it from the graph, and create a
       // new junction for jumps targetting this label.
       junctions[block.entry].outblocks.erase(block.id);
@@ -3245,40 +3245,44 @@ void registerizeHarder(Ref ast) {
     // as pairs of variables that we'd like to have share the same register
     // (the "links").
 
-    var junctionVariables = {};
+    struct JuncVar {
+      StringSet conf, link, excl;
+      IString reg;
+    };
+    std::unordered_map<IString, JuncVar> junctionVariables;
 
-    function initializeJunctionVariable(name) {
-      junctionVariables[name] = { conf: {}, link: {}, excl: {}, reg: null };
-    }
+    auto initializeJunctionVariable = [&](IString name) {
+      junctionVariables[name] = JuncVar(); // XXX
+    };
 
-    for (var i = 0; i < junctions.length; i++) {
-      var junc = junctions[i];
-      for (var name in junc.live) {
-        if (!junctionVariables[name]) initializeJunctionVariable(name);
+    for (int i = 0; i < junctions.size(); i++) {
+      Junction& junc = junctions[i];
+      for (auto name : junc.live) {
+        if (junctionVariables.count(name) == 0) initializeJunctionVariable(name);
         // It conflicts with all other names live at this junction.
-        for (var otherName in junc.live) {
+        for (auto otherName : junc.live) {
           if (otherName == name) continue;
-          junctionVariables[name].conf[otherName] = 1;
+          junctionVariables[name].conf.insert(otherName);
         }
-        for (var b in junc.outblocks) {
-          // It conflits with any output vars of successor blocks,
+        for (auto b : junc.outblocks) {
+          // It conflicts with any output vars of successor blocks,
           // if they're assigned before it goes dead in that block.
-          block = blocks[b];
-          var jSucc = junctions[block.exit];
-          for (var otherName in jSucc.live) {
-            if (junc.live[otherName]) continue;
-            if (block.lastKillLoc[otherName] < block.firstDeadLoc[name]) {
-              if (!junctionVariables[otherName]) initializeJunctionVariable(otherName);
-              junctionVariables[name].conf[otherName] = 1;
-              junctionVariables[otherName].conf[name] = 1;
+          Block* block = blocks[b];
+          Junction& jSucc = junctions[block->exit];
+          for (jSucc.live.has(otherName)) {
+            if (junc.live.has(otherName)) continue;
+            if (block->lastKillLoc[otherName] < block->firstDeadLoc[name]) {
+              if (junctionVariables.count(otherName) == 0) initializeJunctionVariable(otherName);
+              junctionVariables[name].conf.insert(otherName);
+              junctionVariables[otherName].conf.insert(name);
             }
           }
           // It links with any linkages in the outgoing blocks.
-          var linkName = block.link[name];
-          if (linkName && linkName != name) {
-            if (!junctionVariables[linkName]) initializeJunctionVariable(linkName);
-            junctionVariables[name].link[linkName] = 1;
-            junctionVariables[linkName].link[name] = 1;
+          IString linkName = block->link[name];
+          if (!!linkName && linkName != name) {
+            if (junctionVariables.count(linkName) == 0) initializeJunctionVariable(linkName);
+            junctionVariables[name].link.insert(linkName);
+            junctionVariables[linkName].link.insert(name);
           }
         }
       }
@@ -3288,17 +3292,12 @@ void registerizeHarder(Ref ast) {
     // Simple starting point: handle the most-conflicted variables first.
     // This seems to work pretty well.
 
-    var sortedJunctionVariables = keys(junctionVariables);
-    sortedJunctionVariables.sort(function(name1, name2) {
-      var jv1 = junctionVariables[name1];
-      var jv2 = junctionVariables[name2];
-      if (jv1.numConfs == undefined) {
-        jv1.numConfs = setSize(jv1.conf);
-      }
-      if (jv2.numConfs == undefined) {
-        jv2.numConfs = setSize(jv2.conf);
-      }
-      return jv2.numConfs - jv1.numConfs;
+    StringVec sortedJunctionVariables;
+    for (auto pair : junctionVariables) {
+      sortedJunctionVariables.push_back(pair->first);
+    }
+    std::sort(sortedJunctionVariables.begin(), sortedJunctionVariables.end(), [](const IString name1, const IString name2) {
+      return junctionVariables[name2].conf.size() < junctionVariables[name1].conf.size(); //XXX
     });
 
     // We can now assign a register to each junction variable.

From 2736a26860ac01a6623817d65b5ef518401e9056 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= <jujjyl@gmail.com>
Date: Thu, 11 Dec 2014 22:18:21 +0200
Subject: [PATCH 20/31] Apply fix from tests/float_tex.cpp to
 tests/gl_subdata.cpp

---
 tests/gl_subdata.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/tests/gl_subdata.cpp b/tests/gl_subdata.cpp
index 5c6a992686f96..05ec18a4e70d4 100644
--- a/tests/gl_subdata.cpp
+++ b/tests/gl_subdata.cpp
@@ -62,6 +62,13 @@ static void updateFloatTexture() {
     }
     glBindTexture(GL_TEXTURE_2D, nodeTexture);
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, nbNodes, 1, 0, GL_RGBA, GL_FLOAT, data);
+#ifdef __EMSCRIPTEN__ // In GLES2 and WebGL1, we must use unsized texture internal formats.
+    const GLenum internalFormat = GL_RGBA;
+#else
+    // In desktop GL, we can also use sized internal formats.
+    const GLenum internalFormat = GL_RGBA32F;
+#endif
+    glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, nbNodes, 1, 0, GL_RGBA, GL_FLOAT, data);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     glBindTexture(GL_TEXTURE_2D, NULL);
@@ -122,9 +129,11 @@ static void gl_init(void) {
     /* Get the locations of the uniforms so we can access them */
     nodeSamplerLocation      = glGetUniformLocation(program, "nodeInfo");
     glBindAttribLocation(program, 0, "indices");
+#ifndef __EMSCRIPTEN__ // GLES2 & WebGL do not have these, only pre 3.0 desktop GL and compatibility mode GL3.0+ GL do.
     //Enable glPoint size in shader, always enable in Open Gl ES 2.
     glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
     glEnable(GL_POINT_SPRITE);
+#endif
 }
 int main(int argc, char *argv[]) {
     glutInit(&argc, argv);

From 76e60996d1f9596573853111f618cedc76be881c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= <jujjyl@gmail.com>
Date: Thu, 11 Dec 2014 22:19:42 +0200
Subject: [PATCH 21/31] Fix bad glUniform1i call in tests/gl_subdata.cpp

---
 tests/gl_subdata.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/gl_subdata.cpp b/tests/gl_subdata.cpp
index 05ec18a4e70d4..52508a4a3ec35 100644
--- a/tests/gl_subdata.cpp
+++ b/tests/gl_subdata.cpp
@@ -84,7 +84,7 @@ static void glut_draw_callback(void) {
     glActiveTexture(GL_TEXTURE0);
     updateFloatTexture(); //we change the texture each time to create the effect (it is just for the test)
     glBindTexture(GL_TEXTURE_2D, nodeTexture);
-    glUniform1i(nodeSamplerLocation, GL_TEXTURE0);
+    glUniform1i(nodeSamplerLocation, 0);
     glEnableVertexAttribArray(0);
     glBindBuffer(GL_ARRAY_BUFFER, indicesVBO);
     glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, NULL);

From 71f52126725c39a1d9c31c122b243218b487c332 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Thu, 11 Dec 2014 13:18:45 -0800
Subject: [PATCH 22/31] fix some typos in registerizeHarder, and continue on
 conversion

---
 tools/js-optimizer.js         |   4 +-
 tools/optimizer/optimizer.cpp | 137 +++++++++++++++++-----------------
 2 files changed, 72 insertions(+), 69 deletions(-)

diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 462cd717687e1..a00cbd4392723 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -3755,7 +3755,7 @@ function registerizeHarder(ast) {
       // Mark the point at which each input reg becomes dead.
       // Variables alive before this point must not be assigned
       // to that register.
-      var inputVars = {}
+      var inputVars = {};
       var inputDeadLoc = {};
       var inputVarsByReg = {};
       for (var name in jExit.live) {
@@ -3865,7 +3865,7 @@ function registerizeHarder(ast) {
 
     // Assign registers to function params based on entry junction
 
-    var paramRegs = {}
+    var paramRegs = {};
     if (fun[2]) {
       for (var i = 0; i < fun[2].length; i++) {
         var allRegs = allRegsByType[localVars[fun[2][i]]];
diff --git a/tools/optimizer/optimizer.cpp b/tools/optimizer/optimizer.cpp
index e8282027c8aa1..c744fb6dd9bf1 100644
--- a/tools/optimizer/optimizer.cpp
+++ b/tools/optimizer/optimizer.cpp
@@ -3305,40 +3305,40 @@ void registerizeHarder(Ref ast) {
     // one that works, and propagating the choice to linked/conflicted
     // variables as we go.
 
-    function tryAssignRegister(name, reg) {
+    auto tryAssignRegister = [&](IString name, IString reg) {
       // Try to assign the given register to the given variable,
       // and propagate that choice throughout the graph.
       // Returns true if successful, false if there was a conflict.
-      var jv = junctionVariables[name];
-      if (jv.reg != null) {
+      JuncVar& jv = junctionVariables[name];
+      if (!!jv.reg) {
         return jv.reg == reg;
       }
-      if (jv.excl[reg]) {
+      if (jv.excl.has(reg)) {
         return false;
       }
       jv.reg = reg;
       // Exclude use of this register at all conflicting variables.
-      for (var confName in jv.conf) {
-        junctionVariables[confName].excl[reg] = 1;
+      for (auto confName : jv.conf) {
+        junctionVariables[confName].excl.insert(reg);
       }
       // Try to propagate it into linked variables.
       // It's not an error if we can't.
-      for (var linkName in jv.link) {
+      for (auto linkName : jv.link) {
         tryAssignRegister(linkName, reg);
       }
       return true;
-    }
+    };
 
     NEXTVARIABLE:
-    for (var i = 0; i < sortedJunctionVariables.length; i++) {
-      var name = sortedJunctionVariables[i];
+    for (int i = 0; i < sortedJunctionVariables.size(); i++) {
+      IString name = sortedJunctionVariables[i];
       // It may already be assigned due to linked-variable propagation.
-      if (junctionVariables[name].reg != null) {
+      if (!!junctionVariables[name].reg) {
         continue NEXTVARIABLE;
       }
       // Try to use existing registers first.
-      var allRegs = allRegsByType[localVars[name]];
-      for (var reg in allRegs) {
+      auto& allRegs = allRegsByType[asmData.getType(name)];
+      for (auto reg : allRegs) {
         if (tryAssignRegister(name, reg)) {
           continue NEXTVARIABLE;
         }
@@ -3353,92 +3353,95 @@ void registerizeHarder(Ref ast) {
     // that all inter-block variables are in a good state thanks to
     // junction variable consistency.
 
-    for (var i = 0; i < blocks.length; i++) {
-      var block = blocks[i];
-      if (block.nodes.length == 0) continue;
-      var jEnter = junctions[block.entry];
-      int jExit = junctions[block.exit];
+    for (int i = 0; i < blocks.size(); i++) {
+      Block* block = blocks[i];
+      if (block->nodes.size() == 0) continue;
+      int jEnter = junctions[block->entry];
+      int jExit = junctions[block->exit];
       // Mark the point at which each input reg becomes dead.
       // Variables alive before this point must not be assigned
       // to that register.
-      var inputVars = {}
-      var inputDeadLoc = {};
-      var inputVarsByReg = {};
-      for (var name in jExit.live) {
-        if (!(name in block.kill)) {
-          inputVars[name] = 1;
-          var reg = junctionVariables[name].reg;
-          assert(reg != null, 'input variable doesnt have a register');
-          inputDeadLoc[reg] = block.firstDeadLoc[name];
+      StringSet inputVars;
+      std::unordered_set<int> inputDeadLoc;
+      StringSet inputVarsByReg;
+      for (auto name : jExit.live) {
+        if (!block->kill.has(name)) {
+          inputVars.insert(name);
+          IString reg = junctionVariables[name].reg;
+          assert(!!reg); // 'input variable doesnt have a register');
+          inputDeadLoc[reg] = block->firstDeadLoc[name];
           inputVarsByReg[reg] = name;
         }
       }
-      for (var name in block.use) {
-        if (!(name in inputVars)) {
-          inputVars[name] = 1;
-          var reg = junctionVariables[name].reg;
-          assert(reg != null, 'input variable doesnt have a register');
-          inputDeadLoc[reg] = block.firstDeadLoc[name];
+      for (auto name : block->use) {
+        if (!inputVars.has(name)) {
+          inputVars.insert(name);
+          IString reg = junctionVariables[name].reg;
+          assert(!!reg); // 'input variable doesnt have a register');
+          inputDeadLoc[reg] = block->firstDeadLoc[name];
           inputVarsByReg[reg] = name;
         }
       }
-      assert(setSize(setSub(inputVars, jEnter.live)) == 0);
+      // TODO assert(setSize(setSub(inputVars, jEnter.live)) == 0);
       // Scan through backwards, allocating registers on demand.
       // Be careful to avoid conflicts with the input registers.
       // We consume free registers in last-used order, which helps to
       // eliminate "x=y" assignments that are the last use of "y".
-      var assignedRegs = {};
-      var freeRegsByType = copy(allRegsByType);
+      StringSet assignedRegs = {};
+      std::vector<StringVec> freeRegsByType;
+      freeRegsByType.resize(allRegsByType.size());
+      for (int j = 0; j < freeRegsByType.size(); j++) {
+        for (auto pair : allRegsByType[j]) {
+          freeRegsByType[j].push_back(pair.first);
+        }
+      }
       // Begin with all live vars assigned per the exit junction.
-      for (var name in jExit.live) {
-        var reg = junctionVariables[name].reg;
-        assert(reg != null, 'output variable doesnt have a register');
+      for (auto name : jExit.live) {
+        IString reg = junctionVariables[name].reg;
+        assert(!!reg); // 'output variable doesnt have a register');
         assignedRegs[name] = reg;
-        delete freeRegsByType[localVars[name]][reg];
-      }
-      for (var j = 0; j < freeRegsByType.length; j++) {
-        freeRegsByType[j] = keys(freeRegsByType[j]);
+        freeRegsByType[localVars[name]].erase(reg); // XXX assert?
       }
       // Scan through the nodes in sequence, modifying each node in-place
       // and grabbing/freeing registers as needed.
-      var maybeRemoveNodes = [];
-      for (var j = block.nodes.length - 1; j >= 0; j--) {
-        var node = block.nodes[j];
-        var name = node[0] == ASSIGN ? node[2][1] : node[1];
-        var allRegs = allRegsByType[localVars[name]];
-        var freeRegs = freeRegsByType[localVars[name]];
-        var reg = assignedRegs[name];
+      std::vector<std::pair<int, Ref>> maybeRemoveNodes;
+      for (int j = block->nodes.size() - 1; j >= 0; j--) {
+        Ref node = block.nodes[j];
+        IString name = (node[0] == ASSIGN ? node[2][1] : node[1])->getIString();
+        StringStringMap& allRegs = allRegsByType[asmData.getType(name)];
+        StringSet& freeRegs = freeRegsByType[asmData.getType(name)];
+        IString reg = assignedRegs[name];
         if (node[0] == NAME) {
           // A use.  Grab a register if it doesn't have one.
           if (!reg) {
-            if (name in inputVars && j <= block.firstDeadLoc[name]) {
+            if (inputVars.has(name) && j <= block->firstDeadLoc[name]) {
               // Assignment to an input variable, must use pre-assigned reg.
               reg = junctionVariables[name].reg;
               assignedRegs[name] = reg;
-              for (var k = freeRegs.length - 1; k >= 0; k--) {
+              for (int k = freeRegs.size() - 1; k >= 0; k--) {
                 if (freeRegs[k] == reg) {
-                  freeRegs.splice(k, 1);
+                  freeRegs.erase(freeRegs.begin() + k);
                   break;
                 }
               }
             } else {
               // Try to use one of the existing free registers.
               // It must not conflict with an input register.
-              for (var k = freeRegs.length - 1; k >= 0; k--) {
+              for (int k = freeRegs.size() - 1; k >= 0; k--) {
                 reg = freeRegs[k];
                 // Check for conflict with input registers.
-                if (block.firstKillLoc[name] <= inputDeadLoc[reg]) {
+                if (block->firstKillLoc[name] <= inputDeadLoc[reg]) {
                   if (name != inputVarsByReg[reg]) {
                     continue;
                   }
                 }
                 // Found one!
                 assignedRegs[name] = reg;
-                freeRegs.splice(k, 1);
+                freeRegs.erase(freeRegs.begin() + k);
                 break;
               }
               // If we didn't find a suitable register, create a new one.
-              if (!assignedRegs[name]) {
+              if (!assignedRegs.has(name)) {
                 reg = createReg(name);
                 assignedRegs[name] = reg;
               }
@@ -3447,23 +3450,23 @@ void registerizeHarder(Ref ast) {
           node[1] = allRegs[reg];
         } else {
           // A kill. This frees the assigned register.
-          assert(reg, 'live variable doesnt have a reg?')
+          assert(!!reg); //, 'live variable doesnt have a reg?')
           node[2][1] = allRegs[reg];
-          freeRegs.push(reg);
-          delete assignedRegs[name];
-          if (node[3][0] == NAME && node[3][1] in localVars) {
-            maybeRemoveNodes.push([j, node]);
+          freeRegs.push_back(reg);
+          assignedRegs.erase(name);
+          if (node[3][0] == NAME && asmData.isLocal(node[3][1])) {
+            maybeRemoveNodes.push(std::pair<int, Ref>(j, node));
           }
         }
       }
       // If we managed to create an "x=x" assignments, remove them.
-      for (var j = 0; j < maybeRemoveNodes.length; j++) {
-        var node = maybeRemoveNodes[j][1];
+      for (int j = 0; j < maybeRemoveNodes.size(); j++) {
+        Ref node = maybeRemoveNodes[j].second;
         if (node[2][1] == node[3][1]) {
-          if (block.isexpr[maybeRemoveNodes[j][0]]) {
-            morphNode(node, node[2]);
+          if (block->isexpr[maybeRemoveNodes[j].first]) {
+            safeCopy(node, node[2]);
           } else {
-            morphNode(node, [BLOCK, []]);
+            safeCopy(node, makeBlock());
           }
         }
       }

From 1e59231fd3e79913dee4bf6f545b90291c14a61b Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Thu, 11 Dec 2014 13:39:01 -0800
Subject: [PATCH 23/31] finish first conversion pass on registerizeHarder

---
 tools/optimizer/optimizer.cpp | 54 +++++++++++++++++------------------
 1 file changed, 26 insertions(+), 28 deletions(-)

diff --git a/tools/optimizer/optimizer.cpp b/tools/optimizer/optimizer.cpp
index c744fb6dd9bf1..302318e1fcdb4 100644
--- a/tools/optimizer/optimizer.cpp
+++ b/tools/optimizer/optimizer.cpp
@@ -2484,14 +2484,15 @@ void registerizeHarder(Ref ast) {
     // Utilities for allocating register variables.
     // We need distinct register pools for each type of variable.
 
-    std::vector<StringStringMap> allRegsByType;
+    typedef std::unordered_map<int, IString> IntStringMap;
+    std::vector<IntStringMap> allRegsByType;
     allRegsByType.resize(ASM_NONE+1);
     int nextReg = 1;
 
     auto createReg = [&](IString forName) {
       // Create a new register of type suitable for the given variable name.
       AsmType type = asmData.getType(forName);
-      StringIntMap& allRegs = allRegsByType[type];
+      StringStringMap& allRegs = allRegsByType[type];
       reg = nextReg++;
       allRegs[reg] = getRegName(type, reg);
       return reg;
@@ -3474,40 +3475,37 @@ void registerizeHarder(Ref ast) {
 
     // Assign registers to function params based on entry junction
 
-    var paramRegs = {}
-    if (fun[2]) {
-      for (var i = 0; i < fun[2].length; i++) {
-        var allRegs = allRegsByType[localVars[fun[2][i]]];
-        fun[2][i] = allRegs[junctionVariables[fun[2][i]].reg];
-        paramRegs[fun[2][i]] = 1;
+    StringSet paramRegs;
+    if (!!fun[2]) {
+      for (int i = 0; i < fun[2]->size(); i++) {
+        auto& allRegs = allRegsByType[asmData.getType(fun[2][i])];
+        fun[2][i]->setString(allRegs[junctionVariables[fun[2][i]].reg]);
+        paramRegs.insert(fun[2][i]->getIString());
       }
     }
 
     // That's it!
     // Re-construct the function with appropriate variable definitions.
- 
-    var finalAsmData = {
-      params: {},
-      vars: {},
-      inlines: asmData.inlines,
-      ret: asmData.ret,
-    };
-    for (var i = 1; i < nextReg; i++) {
-      var reg;
-      for (var type=0; type<allRegsByType.length; type++) {
-        reg = allRegsByType[type][i];
-        if (reg) break;
-      }
-      if (!paramRegs[reg]) {
-        finalAsmData.vars[reg] = type;
-      } else {
-        finalAsmData.params[reg] = type;
+
+    asmData.locals.clear();
+    asmData.params.clear();
+    asmData.vars.clear();
+    for (int i = 1; i < nextReg; i++) {
+      for (int type = 0; type < allRegsByType.size(); type++) {
+        if (allRegsByType[type].count(i) > 0) {
+          IString reg = allRegsByType[type][i];
+          if (!paramRegs.has(reg)) {
+            asmData.addVar(reg, type);
+          } else {
+            asmData.addParam(reg, type);
+          }
+          break;
+        }
       }
     }
-    denormalizeAsm(fun, finalAsmData);
-
-    vacuum(fun);
+    asmData.denormalize();
 
+    removeAllUselessSubNodes(fun); // XXX vacuum?    vacuum(fun);
   });
 }
 */ // end registerizeHarder

From e7c01d9f8474a9e2e77e55e41120c09ee8e59c23 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Thu, 11 Dec 2014 17:08:24 -0800
Subject: [PATCH 24/31] registerizeHarder cleanups

---
 tools/js-optimizer.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index a00cbd4392723..b94c2beaa0340 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -2872,7 +2872,7 @@ function registerizeHarder(ast) {
     function createReg(forName) {
       // Create a new register of type suitable for the given variable name.
       var allRegs = allRegsByType[localVars[forName]];
-      reg = nextReg++;
+      var reg = nextReg++;
       allRegs[reg] = regPrefixByType[localVars[forName]] + reg;
       return reg;
     }
@@ -3027,7 +3027,7 @@ function registerizeHarder(ast) {
       // Look through value-preserving casts, like "x | 0" => "x"
       if (node[0] === 'binary' && node[1] === '|') {
         if (node[3][0] === 'num' && node[3][1] === 0) {
-            return lookThroughCasts(node[2]);
+          return lookThroughCasts(node[2]);
         }
       }
       return node;
@@ -3202,7 +3202,7 @@ function registerizeHarder(ast) {
             joinJunction(jCondExit);
             joinJunction(jLoop);
             setJunction(jCondExit);
-            joinJunction(jExit)
+            joinJunction(jExit);
           }
           break;
         case 'for':
@@ -3273,7 +3273,7 @@ function registerizeHarder(ast) {
             setJunction(jCheckExit);
           }
           joinJunction(jExit);
-          popActiveLabels()
+          popActiveLabels();
           break;
         case 'return':
           if (node[1]) {

From ca10f1213557f413a109cc4d42b277591d0c46d3 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Thu, 11 Dec 2014 17:09:10 -0800
Subject: [PATCH 25/31] update cashew

---
 tools/optimizer/parser.cpp | 3 ++-
 tools/optimizer/parser.h   | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/tools/optimizer/parser.cpp b/tools/optimizer/parser.cpp
index 4a642a7103b5b..a650f4bd7bb1e 100644
--- a/tools/optimizer/parser.cpp
+++ b/tools/optimizer/parser.cpp
@@ -82,6 +82,7 @@ IString TOPLEVEL("toplevel"),
         NEW("new"),
         ARRAY("array"),
         OBJECT("object"),
+        THROW("throw"),
         SET("=");
 
 IStringSet keywords("var function if else do while for break continue return switch case default throw try catch finally true false null new"),
@@ -115,7 +116,7 @@ struct Init {
 
     precedences.resize(OperatorClass::Tertiary + 1);
 
-    for (int prec = 0; prec < operatorClasses.size(); prec++) {
+    for (size_t prec = 0; prec < operatorClasses.size(); prec++) {
       for (auto curr : operatorClasses[prec].ops) {
         precedences[operatorClasses[prec].type][curr] = prec;
       }
diff --git a/tools/optimizer/parser.h b/tools/optimizer/parser.h
index 1e822e908d6c4..16fc92df54e16 100644
--- a/tools/optimizer/parser.h
+++ b/tools/optimizer/parser.h
@@ -91,6 +91,7 @@ extern IString TOPLEVEL,
                NEW,
                ARRAY,
                OBJECT,
+               THROW,
                SET;
 
 extern IStringSet keywords, allOperators;
@@ -203,7 +204,7 @@ class Parser {
         type = NUMBER;
       } else if (hasChar(OPERATOR_INITS, *src)) {
         switch (*src) {
-          case '!': str = src[1] == '=' ? str = NE : str = L_NOT; break;
+          case '!': str = src[1] == '=' ? NE : L_NOT; break;
           case '%': str = MOD; break;
           case '&': str = AND; break;
           case '*': str = MUL; break;

From b6d37c331ad6c5bf9a0e833ef5a905a434734980 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Thu, 11 Dec 2014 17:09:21 -0800
Subject: [PATCH 26/31] get registerizeHarder port to build

---
 tools/optimizer/optimizer.cpp | 344 ++++++++++++++++++----------------
 1 file changed, 187 insertions(+), 157 deletions(-)

diff --git a/tools/optimizer/optimizer.cpp b/tools/optimizer/optimizer.cpp
index 302318e1fcdb4..beca2b08c4475 100644
--- a/tools/optimizer/optimizer.cpp
+++ b/tools/optimizer/optimizer.cpp
@@ -2,6 +2,7 @@
 #include <cstdio>
 #include <cmath>
 #include <string>
+#include <algorithm>
 
 #include "simple_ast.h"
 
@@ -80,6 +81,18 @@ enum AsmType {
   ASM_NONE = 5 // number of types
 };
 
+AsmType intToAsmType(int type) {
+  switch (type) {
+    case 0: return ASM_INT;
+    case 1: return ASM_DOUBLE;
+    case 2: return ASM_FLOAT;
+    case 3: return ASM_FLOAT32X4;
+    case 4: return ASM_INT32X4;
+    case 5: return ASM_NONE;
+    default: assert(0); return ASM_NONE;
+  }
+}
+
 // forward decls
 struct AsmData;
 AsmType detectType(Ref node, AsmData *asmData=nullptr, bool inVarDef=false);
@@ -2468,14 +2481,13 @@ void registerize(Ref ast) {
 //   * any assignments that we can prove are not subsequently used are removed.
 //     (e.g. unnecessary assignments to the 'label' variable).
 //
-/*
 void registerizeHarder(Ref ast) {
   traverseFunctions(ast, [](Ref fun) {
 
     // Do not try to process non-validating methods, like the heap replacer
     bool abort = false;
-    traverse(fun, function(node, type) {
-      if (type == NEW) abort = true;
+    traversePre(fun, [&abort](Ref node) {
+      if (node[0] == NEW) abort = true;
     });
     if (abort) return;
 
@@ -2492,8 +2504,8 @@ void registerizeHarder(Ref ast) {
     auto createReg = [&](IString forName) {
       // Create a new register of type suitable for the given variable name.
       AsmType type = asmData.getType(forName);
-      StringStringMap& allRegs = allRegsByType[type];
-      reg = nextReg++;
+      IntStringMap& allRegs = allRegsByType[type];
+      int reg = nextReg++;
       allRegs[reg] = getRegName(type, reg);
       return reg;
     };
@@ -2525,15 +2537,22 @@ void registerizeHarder(Ref ast) {
     struct Block {
       int id, entry, exit;
       StringSet labels;
-      std::vector<Node> nodes;
+      std::vector<Ref> nodes;
       std::vector<bool> isexpr;
-      StringSet use;
+      StringIntMap use;
       StringSet kill;
+      StringStringMap link;
+      StringIntMap lastUseLoc;
+      StringIntMap firstDeadLoc;
+      StringIntMap firstKillLoc;
+      StringIntMap lastKillLoc;
+
       Block() : id(-1), entry(-1), exit(-1) {}
     };
     struct ContinueBreak {
       int co, br;
-      BreakContinue(int co_, int br_) : co(co_), br(br_) {}
+      ContinueBreak() : co(-1), br(-1) {}
+      ContinueBreak(int co_, int br_) : co(co_), br(br_) {}
     };
     typedef std::unordered_map<IString, ContinueBreak> LabelState;
 
@@ -2541,7 +2560,7 @@ void registerizeHarder(Ref ast) {
     std::vector<Block*> blocks;
     int currEntryJunction = -1;
     Block* nextBasicBlock = nullptr;
-    bool isInExpr = 0;
+    int isInExpr = 0;
     std::vector<LabelState> activeLabels;
     IString nextLoopLabel;
 
@@ -2557,6 +2576,8 @@ void registerizeHarder(Ref ast) {
       return id;
     };
 
+    std::function<int (int, bool)> joinJunction;
+
     auto markJunction = [&](int id=-1) {
       // Mark current traversal location as a junction.
       // This makes a new basic block exiting at this position.
@@ -2572,7 +2593,7 @@ void registerizeHarder(Ref ast) {
       // This can be used to enter at a previously-declared point.
       // You can't return to a junction with no incoming blocks
       // unless the 'force' parameter is specified.
-      assert(nextBasicBlock.nodes.size() == 0); // refusing to abandon an in-progress basic block
+      assert(nextBasicBlock->nodes.size() == 0); // refusing to abandon an in-progress basic block
       if (force || junctions[id].inblocks.size() > 0) {
         currEntryJunction = id;
       } else {
@@ -2580,7 +2601,7 @@ void registerizeHarder(Ref ast) {
       }
     };
 
-    auto joinJunction = [&](int id, bool force) {
+    joinJunction = [&](int id, bool force) {
       // Complete the pending basic block by exiting at this position.
       // This can be used to exit at a previously-declared point.
       if (currEntryJunction >= 0) {
@@ -2588,8 +2609,8 @@ void registerizeHarder(Ref ast) {
         nextBasicBlock->id = blocks.size();
         nextBasicBlock->entry = currEntryJunction;
         nextBasicBlock->exit = id;
-        junctions[currEntryJunction].outblocks.insert(nextBasicBlock.id);
-        junctions[id].inblocks.insert(nextBasicBlock.id);
+        junctions[currEntryJunction].outblocks.insert(nextBasicBlock->id);
+        junctions[id].inblocks.insert(nextBasicBlock->id);
         blocks.push_back(nextBasicBlock);
       } 
       nextBasicBlock = new Block();
@@ -2623,19 +2644,19 @@ void registerizeHarder(Ref ast) {
       activeLabels.pop_back();
     };
 
-    auto markNonLocalJump = [&](AsmType type, IString label) {
+    auto markNonLocalJump = [&](IString type, IString label) {
       // Complete a block via  RETURN, BREAK or CONTINUE.
       // This joins the targetted junction and then sets the current junction to null.
       // Any code traversed before we get back an existing junction is dead code.
       if (type == RETURN) {
-        joinJunction(EXIT_JUNCTION);
+        joinJunction(EXIT_JUNCTION, false);
       } else {
         assert(activeLabels.back().count(label) > 0); // 'jump to unknown label');
         auto targets = activeLabels.back()[label];
         if (type == CONTINUE) {
-          joinJunction(targets.co);
+          joinJunction(targets.co, false);
         } else if (type == BREAK) {
-          joinJunction(targets.br);
+          joinJunction(targets.br, false);
         } else {
           assert(0); // 'unknown jump node type');
         }
@@ -2646,12 +2667,12 @@ void registerizeHarder(Ref ast) {
     auto addUseNode = [&](Ref node) {
       // Mark a use of the given name node in the current basic block.
       assert(node[0] == NAME); // 'not a use node');
-      Ref name = node[1];
+      IString name = node[1]->getIString();
       if (asmData.isLocal(name)) {
         nextBasicBlock->nodes.push_back(node);
         nextBasicBlock->isexpr.push_back(isInExpr);
         if (nextBasicBlock->kill.count(name) == 0) {
-          nextBasicBlock->use.insert(name);
+          nextBasicBlock->use[name] = 1;
         }
       }
     };
@@ -2661,7 +2682,7 @@ void registerizeHarder(Ref ast) {
       assert(node[0] == ASSIGN); //, 'not a kill node');
       assert(node[1]->isBool(true)); // 'not a kill node');
       assert(node[2][0] == NAME); //, 'not a kill node');
-      Ref name = node[2][1];
+      IString name = node[2][1]->getIString();
       if (asmData.isLocal(name)) {
         nextBasicBlock->nodes.push_back(node);
         nextBasicBlock->isexpr.push_back(isInExpr);
@@ -2669,11 +2690,11 @@ void registerizeHarder(Ref ast) {
       }
     };
 
-    auto lookThroughCasts = [&](Ref node) {
+    std::function<Ref (Ref)> lookThroughCasts = [&](Ref node) {
       // Look through value-preserving casts, like "x | 0" => "x"
       if (node[0] == BINARY && node[1] == OR) {
-        if (node[3][0] == NUM && node[3][1] == 0) {
-            return lookThroughCasts(node[2]);
+        if (node[3][0] == NUM && node[3][1]->getNumber() == 0) {
+          return lookThroughCasts(node[2]);
         }
       }
       return node;
@@ -2682,7 +2703,7 @@ void registerizeHarder(Ref ast) {
     auto addBlockLabel = [&](Ref node) {
       assert(nextBasicBlock->nodes.size() == 0); // 'cant add label to an in-progress basic block')
       if (node[0] == NUM) {
-        nextBasicBlock->labels.insert(node[1]); // XXX?
+        nextBasicBlock->labels.insert(node[1]->getIString()); // XXX?
       }
     };
 
@@ -2716,10 +2737,10 @@ void registerizeHarder(Ref ast) {
         assert(jEntry == ENTRY_JUNCTION);
         int jExit = addJunction();
         assert(jExit == EXIT_JUNCTION);
-        for (int i = 0; i < node[3].length; i++) {
+        for (int i = 0; i < node[3]->size(); i++) {
           buildFlowGraph(node[3][i]);
         }
-        joinJunction(jExit);
+        joinJunction(jExit, false);
       } else if (type == IF) {
         isInExpr++;
         buildFlowGraph(node[1]);
@@ -2736,12 +2757,12 @@ void registerizeHarder(Ref ast) {
           }
           buildFlowGraph(node[2]);
         }
-        joinJunction(jExit);
-        setJunction(jEnter);
+        joinJunction(jExit, false);
+        setJunction(jEnter, false);
         if (!!node[3]) {
           buildFlowGraph(node[3]);
         }
-        joinJunction(jExit);
+        joinJunction(jExit, false);
       } else if (type == CONDITIONAL) {
         isInExpr++;
         // If the conditional has no side-effects, we can treat it as a single
@@ -2761,12 +2782,12 @@ void registerizeHarder(Ref ast) {
           if (!!node[2]) {
             buildFlowGraph(node[2]);
           }
-          joinJunction(jExit);
-          setJunction(jEnter);
+          joinJunction(jExit, false);
+          setJunction(jEnter, false);
           if (!!node[3]) {
             buildFlowGraph(node[3]);
           }
-          joinJunction(jExit);
+          joinJunction(jExit, false);
         }
         isInExpr--;
       } else if (type == WHILE) {
@@ -2778,8 +2799,8 @@ void registerizeHarder(Ref ast) {
           pushActiveLabels(jLoop, jExit);
           buildFlowGraph(node[2]);
           popActiveLabels();
-          joinJunction(jLoop);
-          setJunction(jExit);
+          joinJunction(jLoop, false);
+          setJunction(jExit, false);
         } else {
           int jCond = markJunction();
           int jLoop = addJunction();
@@ -2787,14 +2808,14 @@ void registerizeHarder(Ref ast) {
           isInExpr++;
           buildFlowGraph(node[1]);
           isInExpr--;
-          joinJunction(jLoop);
+          joinJunction(jLoop, false);
           pushActiveLabels(jCond, jExit);
           buildFlowGraph(node[2]);
           popActiveLabels();
-          joinJunction(jCond);
+          joinJunction(jCond, false);
           // An empty basic-block linking condition exit to loop exit.
-          setJunction(jLoop);
-          joinJunction(jExit);
+          setJunction(jLoop, false);
+          joinJunction(jExit, false);
         }
       } else if (type == DO) {
         // Special-case "do {} while (1)" and "do {} while (0)" to use
@@ -2804,15 +2825,15 @@ void registerizeHarder(Ref ast) {
           pushActiveLabels(jExit, jExit);
           buildFlowGraph(node[2]);
           popActiveLabels();
-          joinJunction(jExit);
+          joinJunction(jExit, false);
         } else if (isTrueNode(node[1])) {
           int jLoop = markJunction();
           int jExit = addJunction();
           pushActiveLabels(jLoop, jExit);
           buildFlowGraph(node[2]);
           popActiveLabels();
-          joinJunction(jLoop);
-          setJunction(jExit);
+          joinJunction(jLoop, false);
+          setJunction(jExit, false);
         } else {
           int jLoop = markJunction();
           int jCond = addJunction();
@@ -2821,14 +2842,14 @@ void registerizeHarder(Ref ast) {
           pushActiveLabels(jCond, jExit);
           buildFlowGraph(node[2]);
           popActiveLabels();
-          joinJunction(jCond);
+          joinJunction(jCond, false);
           isInExpr++;
           buildFlowGraph(node[1]);
           isInExpr--;
-          joinJunction(jCondExit);
-          joinJunction(jLoop);
-          setJunction(jCondExit);
-          joinJunction(jExit)
+          joinJunction(jCondExit, false);
+          joinJunction(jLoop, false);
+          setJunction(jCondExit, false);
+          joinJunction(jExit, false);
         }
       } else if (type == FOR) {
         int jTest = addJunction();
@@ -2836,22 +2857,22 @@ void registerizeHarder(Ref ast) {
         int jStep = addJunction();
         int jExit = addJunction();
         buildFlowGraph(node[1]);
-        joinJunction(jTest);
+        joinJunction(jTest, false);
         isInExpr++;
         buildFlowGraph(node[2]);
         isInExpr--;
-        joinJunction(jBody);
+        joinJunction(jBody, false);
         pushActiveLabels(jStep, jExit);
         buildFlowGraph(node[4]);
         popActiveLabels();
-        joinJunction(jStep);
+        joinJunction(jStep, false);
         buildFlowGraph(node[3]);
-        joinJunction(jTest);
-        setJunction(jBody);
-        joinJunction(jExit);
+        joinJunction(jTest, false);
+        setJunction(jBody, false);
+        joinJunction(jExit, false);
       } else if (type == LABEL) {
         assert(BREAK_CAPTURERS.has(node[2][0])); // 'label on non-loop, non-switch statement')
-        nextLoopLabel = node[1];
+        nextLoopLabel = node[1]->getIString();
         buildFlowGraph(node[2]);
       } else if (type == SWITCH) {
         // Emscripten generates switch statements of a very limited
@@ -2867,7 +2888,7 @@ void registerizeHarder(Ref ast) {
         pushActiveLabels(-1, jExit);
         bool hasDefault = false;
         for (int i = 0; i < node[2]->size(); i++) {
-          setJunction(jCheckExit);
+          setJunction(jCheckExit, false);
           // All case clauses are either 'default' or a numeric literal.
           if (!node[2][i][0]) {
             hasDefault = true;
@@ -2884,28 +2905,28 @@ void registerizeHarder(Ref ast) {
           // If there's live code here, assume it jumps to case exit.
           if (currEntryJunction >= 0 && nextBasicBlock->nodes.size() > 0) {
             if (!!node[2][i][0]) {
-              markNonLocalJump(RETURN);
+              markNonLocalJump(RETURN, IString());
             } else {
-              joinJunction(jExit);
+              joinJunction(jExit, false);
             }
           }
         }
         // If there was no default case, we also need an empty block
         // linking straight from the test evaluation to the exit.
         if (!hasDefault) {
-          setJunction(jCheckExit);
+          setJunction(jCheckExit, false);
         }
-        joinJunction(jExit);
-        popActiveLabels()
+        joinJunction(jExit, false);
+        popActiveLabels();
       } else if (type == RETURN) {
         if (!!node[1]) {
           isInExpr++;
           buildFlowGraph(node[1]);
           isInExpr--;
         }
-        markNonLocalJump(type);
+        markNonLocalJump(type->getIString(), IString());
       } else if (type == BREAK || type == CONTINUE) {
-        markNonLocalJump(type, node[1]);
+        markNonLocalJump(type->getIString(), node[1]->getIString());
       } else if (type == ASSIGN) {
         isInExpr++;
         buildFlowGraph(node[3]);
@@ -2947,10 +2968,10 @@ void registerizeHarder(Ref ast) {
         // treat it as a jump to function exit.
         if (!isInExpr && node[1][0] == NAME) {
           if (FUNCTIONS_THAT_ALWAYS_THROW.has(node[1][1])) {
-            markNonLocalJump(RETURN);
+            markNonLocalJump(RETURN, IString());
           }
         }
-      } else if (type == SEQ || type == SUB) {:
+      } else if (type == SEQ || type == SUB) {
         isInExpr++;
         buildFlowGraph(node[1]);
         buildFlowGraph(node[2]);
@@ -2970,7 +2991,7 @@ void registerizeHarder(Ref ast) {
 
     assert(junctions[ENTRY_JUNCTION].inblocks.size() == 0); // 'function entry must have no incoming blocks');
     assert(junctions[EXIT_JUNCTION].outblocks.size() == 0); // 'function exit must have no outgoing blocks');
-    assert(blocks[ENTRY_BLOCK].entry == ENTRY_JUNCTION); //, 'block zero must be the initial block');
+    assert(blocks[ENTRY_BLOCK]->entry == ENTRY_JUNCTION); //, 'block zero must be the initial block');
 
     // Fix up implicit jumps done by assigning to the LABEL variable.
     // If a block ends with an assignment to LABEL and there's another block
@@ -2978,9 +2999,9 @@ void registerizeHarder(Ref ast) {
     // that the former jumps straight to the later.
 
     std::unordered_map<IString, Block*> labelledBlocks;
-    typedef std::pair(Ref, Block*) Jump;
+    typedef std::pair<Ref, Block*> Jump;
     std::vector<Jump> labelledJumps;
-    FINDLABELLEDBLOCKS:
+
     for (int i = 0; i < blocks.size(); i++) {
       Block* block = blocks[i];
       // Does it have any labels as preconditions to its entry?
@@ -2991,12 +3012,12 @@ void registerizeHarder(Ref ast) {
         if (labelledBlocks.count(labelVal) > 0) {
           labelledBlocks.clear();
           labelledJumps.clear();
-          break FINDLABELLEDBLOCKS;
+          goto AFTER_FINDLABELLEDBLOCKS;
         }
         labelledBlocks[labelVal] = block;
       }
       // Does it assign a specific label value at exit?
-      if (block.kill.has(LABEL)) {
+      if (block->kill.has(LABEL)) {
         Ref finalNode = block->nodes.back();
         if (finalNode[0] == ASSIGN && finalNode[2][1] == LABEL) {
           // If labels are computed dynamically then all bets are off.
@@ -3004,46 +3025,49 @@ void registerizeHarder(Ref ast) {
           if (finalNode[3][0] != NUM) {
             labelledBlocks.clear();
             labelledJumps.clear();
-            break FINDLABELLEDBLOCKS;
+            goto AFTER_FINDLABELLEDBLOCKS;
           }
-          labelledJumps.push(Jump(finalNode[3][1], block));
+          labelledJumps.push_back(Jump(finalNode[3][1], block));
         } else { 
           // If label is assigned a non-zero value elsewhere in the block
           // then all bets are off.  This can happen e.g. due to outlining
           // saving/restoring label to the stack.
           for (int j = 0; j < block->nodes.size() - 1; j++) {
-            if (block.nodes[j][0] == ASSIGN && block.nodes[j][2][1] == LABEL) {
-              if (block.nodes[j][3][0] != NUM && block.nodes[j][3][1]->getNumber() != 0) {
+            if (block->nodes[j][0] == ASSIGN && block->nodes[j][2][1] == LABEL) {
+              if (block->nodes[j][3][0] != NUM && block->nodes[j][3][1]->getNumber() != 0) {
                 labelledBlocks.clear();
                 labelledJumps.clear();
-                break FINDLABELLEDBLOCKS;
+                goto AFTER_FINDLABELLEDBLOCKS;
               }
             }
           }
         }
       }
     }
+
+    AFTER_FINDLABELLEDBLOCKS:
+
     for (auto labelVal : labelledBlocks) {
       Block* block = labelVal.second;
       // Disconnect it from the graph, and create a
       // new junction for jumps targetting this label.
-      junctions[block.entry].outblocks.erase(block.id);
-      block.entry = addJunction();
-      junctions[block.entry].outblocks.insert(block.id);
+      junctions[block->entry].outblocks.erase(block->id);
+      block->entry = addJunction();
+      junctions[block->entry].outblocks.insert(block->id);
       // Add a fake use of LABEL to keep it alive in predecessor.
-      block.use.insert(LABEL);
-      block.nodes.insert(block.nodes.begin(), makeName(LABEL));
-      block.isexpr.insert(block.isexpr(), 1);
+      block->use[LABEL] = 1;
+      block->nodes.insert(block->nodes.begin(), makeName(LABEL));
+      block->isexpr.insert(block->isexpr.begin(), 1);
     }
     for (int i = 0; i < labelledJumps.size(); i++) {
       auto labelVal = labelledJumps[i].first;
       auto block = labelledJumps[i].second;
-      Block* targetBlock = labelledBlocks[labelVal];
+      Block* targetBlock = labelledBlocks[labelVal->getIString()];
       if (targetBlock) {
         // Redirect its exit to entry of the target block.
         junctions[block->exit].inblocks.erase(block->id);
         block->exit = targetBlock->entry;
-        junctions[block->exit].inblocks.inasert(block->id);
+        junctions[block->exit].inblocks.insert(block->id);
       }
     }
 
@@ -3056,8 +3080,7 @@ void registerizeHarder(Ref ast) {
 
     auto analyzeJunction = [&](Junction& junc) {
       // Update the live set for this junction.
-      StringSet& live = junc.live;
-      std::unordered_set<int> live;
+      StringSet live;
       for (auto b : junc.outblocks) {
         Block* block = blocks[b];
         StringSet& liveSucc = junctions[block->exit].live;
@@ -3067,9 +3090,10 @@ void registerizeHarder(Ref ast) {
           }
         }
         for (auto name : block->use) {
-          live.insert(name);
+          live.insert(name.first);
         }
       }
+      junc.live = live;
     };
 
     auto analyzeBlock = [&](Block* block) {
@@ -3079,8 +3103,8 @@ void registerizeHarder(Ref ast) {
       // to exit, possibly changing names via simple 'x=y' assignments.
       // As we go, we eliminate assignments if the variable is not
       // subsequently used.
-      auto live = junctions[block.exit].live;
-      StringSet use;
+      auto live = junctions[block->exit].live;
+      StringIntMap use;
       StringSet kill;
       StringStringMap link;
       StringIntMap lastUseLoc;
@@ -3124,8 +3148,8 @@ void registerizeHarder(Ref ast) {
               IString oldLink = link[name];
               link.erase(name);
               if (node[3][0] == NAME) {
-                if (asmData.isLocal(node[3][1])) {
-                  link[node[3][1]] = oldLink;
+                if (asmData.isLocal(node[3][1]->getIString())) {
+                  link[node[3][1]->getIString()] = oldLink;
                 }
               }
             }
@@ -3133,32 +3157,32 @@ void registerizeHarder(Ref ast) {
             // The result of this assignment is never used, so delete it.
             // We may need to keep the RHS for its value or its side-effects.
             auto removeUnusedNodes = [&](int j, int n) {
-              for (auto name : lastUseLoc) {
-                lastUseLoc[name] -= n;
+              for (auto pair : lastUseLoc) {
+                pair.second -= n;
               }
-              for (auto name : firstKillLoc) {
-                firstKillLoc[name] -= n;
+              for (auto pair : firstKillLoc) {
+                pair.second -= n;
               }
-              for (auto name : lastKillLoc) {
-                lastKillLoc[name] -= n;
+              for (auto pair : lastKillLoc) {
+                pair.second -= n;
               }
-              for (auto name : firstDeadLoc) {
-                firstDeadLoc[name] -= n;
+              for (auto pair : firstDeadLoc) {
+                pair.second -= n;
               }
               block->nodes.erase(block->nodes.begin() + j, block->nodes.begin() + j + n);
               block->isexpr.erase(block->isexpr.begin() + j, block->isexpr.begin() + j + n);
-            }
-            if (block.isexpr[j] || hasSideEffects(node[3])) {
+            };
+            if (block->isexpr[j] || hasSideEffects(node[3])) {
               safeCopy(node, node[3]);
               removeUnusedNodes(j, 1);
             } else {
               int numUsesInExpr = 0;
               traversePre(node[3], [&](Ref node) {
-                if (node[0] == NAME && asmData.isLocal(node[1])) {
+                if (node[0] == NAME && asmData.isLocal(node[1]->getIString())) {
                   numUsesInExpr++;
                 }
               });
-              morphNode(node, makeBlock());
+              safeCopy(node, makeBlock());
               j = j - numUsesInExpr;
               removeUnusedNodes(j, 1 + numUsesInExpr);
             }
@@ -3166,13 +3190,13 @@ void registerizeHarder(Ref ast) {
         }
       }
       // XXX efficiency
-      block.use = use;
-      block.kill = kill;
-      block.link = link;
-      block.lastUseLoc = lastUseLoc;
-      block.firstDeadLoc = firstDeadLoc;
-      block.firstKillLoc = firstKillLoc;
-      block.lastKillLoc = lastKillLoc;
+      block->use = use;
+      block->kill = kill;
+      block->link = link;
+      block->lastUseLoc = lastUseLoc;
+      block->firstDeadLoc = firstDeadLoc;
+      block->firstKillLoc = firstKillLoc;
+      block->lastKillLoc = lastKillLoc;
     };
 
     std::unordered_set<int> jWorklistMap;
@@ -3204,10 +3228,10 @@ void registerizeHarder(Ref ast) {
           // Live set changed, updated predecessor blocks and junctions.
           for (auto b : junc.inblocks) {
             if (bWorklistMap.count(b) == 0) {
-              bWorklistMap.insert(b)
+              bWorklistMap.insert(b);
               bWorklist.push_back(b);
             }
-            int jPred = blocks[b].entry;
+            int jPred = blocks[b]->entry;
             if (jWorklistMap.count(jPred) == 0) {
               jWorklistMap.insert(jPred);
               jWorklist.push_back(jPred);
@@ -3220,13 +3244,13 @@ void registerizeHarder(Ref ast) {
         Block* block = blocks[bWorklist.back()];
         bWorklist.pop_back();
         bWorklistMap.erase(block->id);
-        StringSet oldUse = block->use;
+        auto oldUse = block->use;
         analyzeBlock(block);
         if (oldUse != block->use) {
           // The use set changed, re-process the entry junction.
           if (jWorklistMap.count(block->entry) == 0) {
-            jWorklistMap.insert(block.entry);
-            jWorklist.push_back(block.entry);
+            jWorklistMap.insert(block->entry);
+            jWorklist.push_back(block->entry);
           }
         }
       }
@@ -3247,8 +3271,10 @@ void registerizeHarder(Ref ast) {
     // (the "links").
 
     struct JuncVar {
-      StringSet conf, link, excl;
-      IString reg;
+      StringSet conf, link;
+      std::unordered_set<int> excl;
+      int reg;
+      JuncVar() : reg(-1) {}
     };
     std::unordered_map<IString, JuncVar> junctionVariables;
 
@@ -3270,7 +3296,7 @@ void registerizeHarder(Ref ast) {
           // if they're assigned before it goes dead in that block.
           Block* block = blocks[b];
           Junction& jSucc = junctions[block->exit];
-          for (jSucc.live.has(otherName)) {
+          for (auto otherName : jSucc.live) {
             if (junc.live.has(otherName)) continue;
             if (block->lastKillLoc[otherName] < block->firstDeadLoc[name]) {
               if (junctionVariables.count(otherName) == 0) initializeJunctionVariable(otherName);
@@ -3295,9 +3321,9 @@ void registerizeHarder(Ref ast) {
 
     StringVec sortedJunctionVariables;
     for (auto pair : junctionVariables) {
-      sortedJunctionVariables.push_back(pair->first);
+      sortedJunctionVariables.push_back(pair.first);
     }
-    std::sort(sortedJunctionVariables.begin(), sortedJunctionVariables.end(), [](const IString name1, const IString name2) {
+    std::sort(sortedJunctionVariables.begin(), sortedJunctionVariables.end(), [&](const IString name1, const IString name2) {
       return junctionVariables[name2].conf.size() < junctionVariables[name1].conf.size(); //XXX
     });
 
@@ -3306,15 +3332,15 @@ void registerizeHarder(Ref ast) {
     // one that works, and propagating the choice to linked/conflicted
     // variables as we go.
 
-    auto tryAssignRegister = [&](IString name, IString reg) {
+    std::function<bool (IString, int)> tryAssignRegister = [&](IString name, int reg) {
       // Try to assign the given register to the given variable,
       // and propagate that choice throughout the graph.
       // Returns true if successful, false if there was a conflict.
       JuncVar& jv = junctionVariables[name];
-      if (!!jv.reg) {
+      if (jv.reg > 0) {
         return jv.reg == reg;
       }
-      if (jv.excl.has(reg)) {
+      if (jv.excl.count(reg) > 0) {
         return false;
       }
       jv.reg = reg;
@@ -3330,20 +3356,22 @@ void registerizeHarder(Ref ast) {
       return true;
     };
 
-    NEXTVARIABLE:
     for (int i = 0; i < sortedJunctionVariables.size(); i++) {
       IString name = sortedJunctionVariables[i];
       // It may already be assigned due to linked-variable propagation.
       if (!!junctionVariables[name].reg) {
-        continue NEXTVARIABLE;
+        continue;
       }
       // Try to use existing registers first.
       auto& allRegs = allRegsByType[asmData.getType(name)];
+      bool moar = false;
       for (auto reg : allRegs) {
-        if (tryAssignRegister(name, reg)) {
-          continue NEXTVARIABLE;
+        if (tryAssignRegister(name, reg.first)) {
+          moar = true;
+          break;
         }
       }
+      if (moar) continue;
       // They're all taken, create a new one.
       tryAssignRegister(name, createReg(name));
     }
@@ -3357,28 +3385,29 @@ void registerizeHarder(Ref ast) {
     for (int i = 0; i < blocks.size(); i++) {
       Block* block = blocks[i];
       if (block->nodes.size() == 0) continue;
-      int jEnter = junctions[block->entry];
-      int jExit = junctions[block->exit];
+      Junction& jEnter = junctions[block->entry];
+      Junction& jExit = junctions[block->exit];
       // Mark the point at which each input reg becomes dead.
       // Variables alive before this point must not be assigned
       // to that register.
       StringSet inputVars;
-      std::unordered_set<int> inputDeadLoc;
-      StringSet inputVarsByReg;
+      std::unordered_map<int, int> inputDeadLoc;
+      std::unordered_map<int, IString> inputVarsByReg;
       for (auto name : jExit.live) {
         if (!block->kill.has(name)) {
           inputVars.insert(name);
-          IString reg = junctionVariables[name].reg;
-          assert(!!reg); // 'input variable doesnt have a register');
+          int reg = junctionVariables[name].reg;
+          assert(reg > 0); // 'input variable doesnt have a register');
           inputDeadLoc[reg] = block->firstDeadLoc[name];
           inputVarsByReg[reg] = name;
         }
       }
-      for (auto name : block->use) {
+      for (auto pair : block->use) {
+        IString name = pair.first;
         if (!inputVars.has(name)) {
           inputVars.insert(name);
-          IString reg = junctionVariables[name].reg;
-          assert(!!reg); // 'input variable doesnt have a register');
+          int reg = junctionVariables[name].reg;
+          assert(reg > 0); // 'input variable doesnt have a register');
           inputDeadLoc[reg] = block->firstDeadLoc[name];
           inputVarsByReg[reg] = name;
         }
@@ -3388,30 +3417,31 @@ void registerizeHarder(Ref ast) {
       // Be careful to avoid conflicts with the input registers.
       // We consume free registers in last-used order, which helps to
       // eliminate "x=y" assignments that are the last use of "y".
-      StringSet assignedRegs = {};
-      std::vector<StringVec> freeRegsByType;
-      freeRegsByType.resize(allRegsByType.size());
-      for (int j = 0; j < freeRegsByType.size(); j++) {
-        for (auto pair : allRegsByType[j]) {
-          freeRegsByType[j].push_back(pair.first);
-        }
-      }
+      StringIntMap assignedRegs;
+      auto freeRegsByTypePre = allRegsByType; // XXX copy
       // Begin with all live vars assigned per the exit junction.
       for (auto name : jExit.live) {
-        IString reg = junctionVariables[name].reg;
-        assert(!!reg); // 'output variable doesnt have a register');
+        int reg = junctionVariables[name].reg;
+        assert(reg > 0); // 'output variable doesnt have a register');
         assignedRegs[name] = reg;
-        freeRegsByType[localVars[name]].erase(reg); // XXX assert?
+        freeRegsByTypePre[asmData.getType(name)].erase(reg); // XXX assert?
+      }
+      std::vector<std::vector<int>> freeRegsByType;
+      freeRegsByType.resize(freeRegsByTypePre.size());
+      for (int j = 0; j < freeRegsByTypePre.size(); j++) {
+        for (auto pair : freeRegsByTypePre[j]) {
+          freeRegsByType[j].push_back(pair.first);
+        }
       }
       // Scan through the nodes in sequence, modifying each node in-place
       // and grabbing/freeing registers as needed.
       std::vector<std::pair<int, Ref>> maybeRemoveNodes;
       for (int j = block->nodes.size() - 1; j >= 0; j--) {
-        Ref node = block.nodes[j];
+        Ref node = block->nodes[j];
         IString name = (node[0] == ASSIGN ? node[2][1] : node[1])->getIString();
-        StringStringMap& allRegs = allRegsByType[asmData.getType(name)];
-        StringSet& freeRegs = freeRegsByType[asmData.getType(name)];
-        IString reg = assignedRegs[name];
+        IntStringMap& allRegs = allRegsByType[asmData.getType(name)];
+        std::vector<int>& freeRegs = freeRegsByType[asmData.getType(name)];
+        int reg = assignedRegs[name]; // XXX may insert a zero
         if (node[0] == NAME) {
           // A use.  Grab a register if it doesn't have one.
           if (!reg) {
@@ -3448,15 +3478,15 @@ void registerizeHarder(Ref ast) {
               }
             }
           }
-          node[1] = allRegs[reg];
+          node[1]->setString(allRegs[reg]);
         } else {
           // A kill. This frees the assigned register.
           assert(!!reg); //, 'live variable doesnt have a reg?')
-          node[2][1] = allRegs[reg];
+          node[2][1]->setString(allRegs[reg]);
           freeRegs.push_back(reg);
           assignedRegs.erase(name);
-          if (node[3][0] == NAME && asmData.isLocal(node[3][1])) {
-            maybeRemoveNodes.push(std::pair<int, Ref>(j, node));
+          if (node[3][0] == NAME && asmData.isLocal(node[3][1]->getIString())) {
+            maybeRemoveNodes.push_back(std::pair<int, Ref>(j, node));
           }
         }
       }
@@ -3478,8 +3508,8 @@ void registerizeHarder(Ref ast) {
     StringSet paramRegs;
     if (!!fun[2]) {
       for (int i = 0; i < fun[2]->size(); i++) {
-        auto& allRegs = allRegsByType[asmData.getType(fun[2][i])];
-        fun[2][i]->setString(allRegs[junctionVariables[fun[2][i]].reg]);
+        auto& allRegs = allRegsByType[asmData.getType(fun[2][i]->getIString())];
+        fun[2][i]->setString(allRegs[junctionVariables[fun[2][i]->getIString()].reg]);
         paramRegs.insert(fun[2][i]->getIString());
       }
     }
@@ -3495,9 +3525,9 @@ void registerizeHarder(Ref ast) {
         if (allRegsByType[type].count(i) > 0) {
           IString reg = allRegsByType[type][i];
           if (!paramRegs.has(reg)) {
-            asmData.addVar(reg, type);
+            asmData.addVar(reg, intToAsmType(type));
           } else {
-            asmData.addParam(reg, type);
+            asmData.addParam(reg, intToAsmType(type));
           }
           break;
         }
@@ -3508,7 +3538,7 @@ void registerizeHarder(Ref ast) {
     removeAllUselessSubNodes(fun); // XXX vacuum?    vacuum(fun);
   });
 }
-*/ // end registerizeHarder
+// end registerizeHarder
 
 // minified names generation
 StringSet RESERVED("do if in for new try var env let");

From 9d50d2f267d5b88c40b01c608a1f1eae3000e7ba Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Thu, 11 Dec 2014 18:24:04 -0800
Subject: [PATCH 27/31] make registerizeHarder testcase valid asm.js

---
 tools/test-js-optimizer-asm-regs-harder-output.js | 2 +-
 tools/test-js-optimizer-asm-regs-harder.js        | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/tools/test-js-optimizer-asm-regs-harder-output.js b/tools/test-js-optimizer-asm-regs-harder-output.js
index c3b326f61feef..25e4e2db085df 100644
--- a/tools/test-js-optimizer-asm-regs-harder-output.js
+++ b/tools/test-js-optimizer-asm-regs-harder-output.js
@@ -116,7 +116,7 @@ function linkedVars() {
  while (1) {
   i2 = 9;
   i1 = 5;
-  while (i2 > 0 || i1 > 0) {
+  while (i2 > 0 | i1 > 0) {
    if (i2 < i1) {
     i2 = i2 - 1;
    } else {
diff --git a/tools/test-js-optimizer-asm-regs-harder.js b/tools/test-js-optimizer-asm-regs-harder.js
index fa72aab8135a2..faa2845920d62 100644
--- a/tools/test-js-optimizer-asm-regs-harder.js
+++ b/tools/test-js-optimizer-asm-regs-harder.js
@@ -108,7 +108,7 @@ function iffey() {
 }
 function labelledJump(x) {
  x = x | 0;
- var label = 0
+ var label = 0;
  // y and z don't conflict, but only if you know about labelled jumps.
  var y = 0, z = 0;
  y = 2;
@@ -129,7 +129,7 @@ function linkedVars() {
  while (1) {
   outer1 = 9;
   outer2 = 5;
-  while (outer1 > 0 || outer2 > 0) {
+  while ((outer1 > 0) | (outer2 > 0)) {
    // All these copy assignment should be eliminated by var sharing.
    inner1_0 = outer1;
    inner2_0 = outer2;

From b6e20f6d60cb6157c29afcd86d536ec72fb7604b Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 12 Dec 2014 10:37:57 -0800
Subject: [PATCH 28/31] fix sanity.test_closure_compiler

---
 tests/test_sanity.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/test_sanity.py b/tests/test_sanity.py
index 24b352be3778f..02c0954bed90d 100644
--- a/tests/test_sanity.py
+++ b/tests/test_sanity.py
@@ -149,12 +149,12 @@ def test_closure_compiler(self):
     f = open(CONFIG_FILE, 'a')
     f.write('CLOSURE_COMPILER = "/tmp/nowhere/nothingtoseehere/kjadsfkjwelkjsdfkqgas/nonexistent.txt"\n')
     f.close()
-    output = self.check_working([EMCC, '-O2', '-s', 'ASM_JS=0', '--closure', '1', 'tests/hello_world.cpp'], CLOSURE_FATAL)
+    output = self.check_working([EMCC, '-O2', '-s', '--closure', '1', 'tests/hello_world.cpp'], CLOSURE_FATAL)
 
     # With a working path, all is well
     restore()
     try_delete('a.out.js')
-    output = self.check_working([EMCC, '-O2', '-s', 'ASM_JS=0', '--closure', '1', 'tests/hello_world.cpp'], '')
+    output = self.check_working([EMCC, '-O2', '-s', '--closure', '1', 'tests/hello_world.cpp'], '')
     assert os.path.exists('a.out.js'), output
 
   def test_llvm(self):

From a89a6711ddba719925c892471734fcc2a3b480f1 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 12 Dec 2014 10:42:18 -0800
Subject: [PATCH 29/31] remove some non-asm tests from other.test_emcc

---
 tests/test_other.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/test_other.py b/tests/test_other.py
index 6b5807f05ee9e..e88ff62376f10 100644
--- a/tests/test_other.py
+++ b/tests/test_other.py
@@ -118,13 +118,13 @@ def test_emcc(self):
         (['-o', 'something.js', '-O2'],                   2, None, 0, 1),
         (['-o', 'something.js', '-O2', '-g'],             2, None, 0, 0),
         (['-o', 'something.js', '-Os'],                   2, None, 0, 1),
-        (['-o', 'something.js', '-O3', '-s', 'ASM_JS=0'], 3, None, 0, 1),
+        (['-o', 'something.js', '-O3'],                   3, None, 0, 1),
         # and, test compiling to bitcode first
         (['-o', 'something.bc'], 0, [],      0, 0),
         (['-o', 'something.bc', '-O0'], 0, [], 0, 0),
         (['-o', 'something.bc', '-O1'], 1, ['-O1'], 0, 0),
         (['-o', 'something.bc', '-O2'], 2, ['-O2'], 0, 0),
-        (['-o', 'something.bc', '-O3'], 3, ['-O3', '-s', 'ASM_JS=0'], 0, 0),
+        (['-o', 'something.bc', '-O3'], 3, ['-O3'], 0, 0),
         (['-O1', '-o', 'something.bc'], 1, [], 0, 0),
       ]:
         print params, opt_level, bc_params, closure, has_malloc

From d84aa198c20dc6d0c5ff247a10c3fd95f38cb0ca Mon Sep 17 00:00:00 2001
From: Jukka Jylanki <jujjyl@gmail.com>
Date: Fri, 12 Dec 2014 22:18:26 +0200
Subject: [PATCH 30/31] Remove timing dependency on browser.test_glgears_proxy
 by rendering a static nonanimated version of the gears in each rAF.

---
 tests/hello_world_gles_proxy.c | 9 +++++++++
 tests/test_browser.py          | 2 +-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/tests/hello_world_gles_proxy.c b/tests/hello_world_gles_proxy.c
index 1a8e5f61da278..7e977fed64d0b 100644
--- a/tests/hello_world_gles_proxy.c
+++ b/tests/hello_world_gles_proxy.c
@@ -40,6 +40,7 @@
 
 #define _GNU_SOURCE
 
+#include <assert.h>
 #include <math.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -613,11 +614,19 @@ gears_idle(void)
    static double tRot0 = -1.0, tRate0 = -1.0;
    double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
 
+#ifdef STATIC_GEARS
+   assert(t - tRot0 > 0); // Test that time does advance even though we are rendering a static image.
+#endif
+
    if (tRot0 < 0.0)
       tRot0 = t;
    dt = t - tRot0;
    tRot0 = t;
 
+#ifdef STATIC_GEARS
+   dt = t = 0.0; // Render the gears in a static position.
+#endif
+
    /* advance rotation for next frame */
    angle += 70.0 * dt;  /* 70 degrees per second */
    if (angle > 3600.0)
diff --git a/tests/test_browser.py b/tests/test_browser.py
index 51f257412a6a4..ddd4849ac6dbb 100644
--- a/tests/test_browser.py
+++ b/tests/test_browser.py
@@ -758,7 +758,7 @@ def test_sdl_canvas_proxy(self):
     self.btest('sdl_canvas_proxy.c', reference='sdl_canvas_proxy.png', args=['--proxy-to-worker', '--preload-file', 'data.txt'], manual_reference=True, post_build=self.post_manual_reftest)
 
   def test_glgears_proxy(self):
-    self.btest('hello_world_gles_proxy.c', reference='gears.png', args=['--proxy-to-worker', '-s', 'GL_TESTING=1'], manual_reference=True, post_build=self.post_manual_reftest)
+    self.btest('hello_world_gles_proxy.c', reference='gears.png', args=['--proxy-to-worker', '-s', 'GL_TESTING=1', '-DSTATIC_GEARS=1'], manual_reference=True, post_build=self.post_manual_reftest)
 
     # test noProxy option applied at runtime
 

From ed94894b305bb09808862523417821d3b26c8656 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 12 Dec 2014 15:48:49 -0800
Subject: [PATCH 31/31] 1.28.0

---
 emscripten-version.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/emscripten-version.txt b/emscripten-version.txt
index 362c239d66da5..b1a41036cde93 100644
--- a/emscripten-version.txt
+++ b/emscripten-version.txt
@@ -1,2 +1,2 @@
-1.27.2
+1.28.0