diff --git a/src/backends/glr.c b/src/backends/glr.c
index 2978e62daf22854cd2410b1d89f94c8eedfcea5a..411171b797df8f913fba764f8c89fecd16914ac9 100644
--- a/src/backends/glr.c
+++ b/src/backends/glr.c
@@ -70,15 +70,54 @@ static const HLRAction *handle_conflict(HSlist *engines, const HLREngine *engine
   return branches->head->elem;
 }
 
-static HLREngine *handle_demerge(HSlist *engines, HLREngine *engine,
-                                 const HLRAction *reduce)
+static HSlist *demerge_stack(HSlistNode *bottom, HSlistNode *mp, HSlist *stack)
 {
-  return engine; // XXX
+  HArena *arena = stack->arena;
+
+  HSlist *ret = h_slist_new(arena);
+
+  // copy the stack from the top
+  HSlistNode **y = &ret->head;
+  for(HSlistNode *x=stack->head; x && x!=mp; x=x->next) {
+    HSlistNode *node = h_arena_malloc(arena, sizeof(HSlistNode));
+    node->elem = x->elem;
+    node->next = NULL;
+    *y = node;
+    y = &node->next;
+  }
+  *y = bottom;  // attach the ancestor stack
+
+  return ret;
+}
 
-  for(size_t i=0; i<reduce->production.length; i++) {
-    // XXX if stack hits bottom, demerge
+static void demerge(HSlist *engines, HLREngine *engine,
+                    const HLRAction *action, size_t depth)
+{
+  // no-op on engines that are not merged
+  if(!engine->merged)
+    return;
+
+  HSlistNode *p = engine->stack->head;
+  for(size_t i=0; i<depth; i++) {
+    // if stack hits mergepoint, respawn ancestor
+    if(p == engine->mp) {
+      HLREngine *eng = engine->merged;
+      eng->stack = demerge_stack(eng->stack->head, engine->mp, engine->stack);
+      demerge(engines, eng, action, depth-i);
+      
+      // call step and stow on restored ancestor
+      h_lrengine_step(eng, action);
+      stow_engine(engines, eng);
+      break;
+    }
+    p = p->next;
   }
-  // XXX call step and stow on the newly-created engines
+}
+
+static inline void
+handle_demerge(HSlist *engines, HLREngine *engine, const HLRAction *reduce)
+{
+  demerge(engines, engine, reduce, reduce->production.length);
 }
 
 HParseResult *h_glr_parse(HAllocator* mm__, const HParser* parser, HInputStream* stream)
@@ -119,8 +158,8 @@ HParseResult *h_glr_parse(HAllocator* mm__, const HParser* parser, HInputStream*
           // fork engine on conflicts
           action = handle_conflict(engines, engine, action->branches);
         } else if(action->type == HLR_REDUCE) {
-          // demerge as needed to ensure that stacks are deep enough
-          engine = handle_demerge(engines, engine, action);
+          // demerge/respawn as needed
+          handle_demerge(engines, engine, action);
         }
       }
 
diff --git a/src/backends/lr.c b/src/backends/lr.c
index bb20f7148de97ad3e8284c5ae03b2338e7c2966e..c481d291fe881249b4c2b2aa17cff945ab289f06 100644
--- a/src/backends/lr.c
+++ b/src/backends/lr.c
@@ -210,6 +210,7 @@ HLREngine *h_lrengine_new(HArena *arena, HArena *tarena, const HLRTable *table,
   engine->stack = h_slist_new(tarena);
   engine->input = *stream;
   engine->merged = NULL;
+  engine->mp = NULL;
   engine->arena = arena;
   engine->tarena = tarena;
 
diff --git a/src/backends/lr.h b/src/backends/lr.h
index 5febc2470dc3bdcdd32454573fbcdfd041f6fc08..ab48633514bf6565924198bc723a42ca96662c15 100644
--- a/src/backends/lr.h
+++ b/src/backends/lr.h
@@ -74,7 +74,8 @@ typedef struct HLREngine_ {
   HSlist *stack;        // holds pairs: (saved state, semantic value)
   HInputStream input;
 
-  HSlist *merged;       // saved ancestor engines that merged to form this one
+  struct HLREngine_ *merged;    // ancestor merged into this engine at mp
+  HSlistNode *mp;               // mergepoint: stack->head at time of merge
 
   HArena *arena;        // will hold the results
   HArena *tarena;       // tmp, deleted after parse