[subset] Improve sharing of Ligature subtables.

Ligature subtables use virtual links to enforce an ordering constraint between the subtables and the coverage table. Unfortunately this has the sideeffect of prevent the subtables from being shared by another Ligature with a different coverage table since object equality compares all links real and virtual. This change makes virtual links stored separately from real links and updates the equality check to only check real links. If an object is de-duped any virtual links it has are merged into the object that replaces it.
diff --git a/src/hb-repacker.hh b/src/hb-repacker.hh
index 26faa56..5d31844 100644
--- a/src/hb-repacker.hh
+++ b/src/hb-repacker.hh
@@ -100,7 +100,7 @@
 
     bool is_leaf () const
     {
-      return !obj.links.length;
+      return !obj.real_links.length && !obj.virtual_links.length;
     }
 
     void raise_priority ()
@@ -164,9 +164,11 @@
       if (check_success (!vertices_.in_error ()))
         v->obj = *objects[i];
       if (!removed_nil) continue;
-      for (unsigned i = 0; i < v->obj.links.length; i++)
-        // Fix indices to account for removed nil object.
-        v->obj.links[i].objidx--;
+      // Fix indices to account for removed nil object.
+      for (auto& l : hb_concat (v->obj.real_links.writer (),
+                                v->obj.virtual_links.writer ())) {
+        l.objidx--;
+      }
     }
   }
 
@@ -215,7 +217,8 @@
 
       memcpy (start, vertices_[i].obj.head, size);
 
-      for (const auto& link : vertices_[i].obj.links)
+      // Only real links needs to be serialized.
+      for (const auto& link : vertices_[i].obj.real_links)
         serialize_link (link, start, c);
 
       // All duplications are already encoded in the graph, so don't
@@ -260,7 +263,7 @@
       sorted_graph[new_id] = next;
       id_map[next_id] = new_id--;
 
-      for (const auto& link : next.obj.links) {
+      for (const auto& link : hb_concat (next.obj.real_links, next.obj.virtual_links)) {
         removed_edges[link.objidx]++;
         if (!(vertices_[link.objidx].incoming_edges () - removed_edges[link.objidx]))
           queue.push (link.objidx);
@@ -314,7 +317,7 @@
       sorted_graph[new_id] = next;
       id_map[next_id] = new_id--;
 
-      for (const auto& link : next.obj.links) {
+      for (const auto& link : hb_concat (next.obj.real_links, next.obj.virtual_links)) {
         removed_edges[link.objidx]++;
         if (!(vertices_[link.objidx].incoming_edges () - removed_edges[link.objidx]))
           // Add the order that the links were encountered to the priority.
@@ -348,7 +351,8 @@
     hb_set_t roots;
     for (unsigned i = 0; i <= root_index; i++)
     {
-      for (auto& l : vertices_[i].obj.links)
+      // Only real links can form 32 bit spaces
+      for (auto& l : vertices_[i].obj.real_links)
       {
         if (l.width == 4 && !l.is_signed)
         {
@@ -466,7 +470,8 @@
 
   void find_subgraph (unsigned node_idx, hb_hashmap_t<unsigned, unsigned>& subgraph)
   {
-    for (const auto& link : vertices_[node_idx].obj.links)
+    for (const auto& link : hb_concat (vertices_[node_idx].obj.real_links,
+                                       vertices_[node_idx].obj.virtual_links))
     {
       if (subgraph.has (link.objidx))
       {
@@ -482,7 +487,8 @@
   {
     if (subgraph.has (node_idx)) return;
     subgraph.add (node_idx);
-    for (const auto& link : vertices_[node_idx].obj.links)
+    for (const auto& link : hb_concat (vertices_[node_idx].obj.real_links,
+                                       vertices_[node_idx].obj.virtual_links))
       find_subgraph (link.objidx, subgraph);
   }
 
@@ -497,7 +503,8 @@
       return;
 
     index_map.set (node_idx, duplicate (node_idx));
-    for (const auto& l : object (node_idx).links) {
+    for (const auto& l : hb_concat (object (node_idx).real_links,
+                                    object (node_idx).virtual_links)) {
       duplicate_subgraph (l.objidx, index_map);
     }
   }
@@ -523,13 +530,19 @@
     clone->parents.reset ();
 
     unsigned clone_idx = vertices_.length - 2;
-    for (const auto& l : child.obj.links)
+    for (const auto& l : child.obj.real_links)
     {
-      clone->obj.links.push (l);
+      clone->obj.real_links.push (l);
+      vertices_[l.objidx].parents.push (clone_idx);
+    }
+    for (const auto& l : child.obj.virtual_links)
+    {
+      clone->obj.virtual_links.push (l);
       vertices_[l.objidx].parents.push (clone_idx);
     }
 
-    check_success (!clone->obj.links.in_error ());
+    check_success (!clone->obj.real_links.in_error ());
+    check_success (!clone->obj.virtual_links.in_error ());
 
     // The last object is the root of the graph, so swap back the root to the end.
     // The root's obj idx does change, however since it's root nothing else refers to it.
@@ -539,7 +552,8 @@
     vertices_[vertices_.length - 1] = root;
 
     // Since the root moved, update the parents arrays of all children on the root.
-    for (const auto& l : root.obj.links)
+    for (const auto& l : hb_concat (root.obj.real_links,
+                                    root.obj.virtual_links))
       vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ());
 
     return clone_idx;
@@ -555,7 +569,8 @@
     update_parents ();
 
     unsigned links_to_child = 0;
-    for (const auto& l : vertices_[parent_idx].obj.links)
+    for (const auto& l : hb_concat (vertices_[parent_idx].obj.real_links,
+                                    vertices_[parent_idx].obj.virtual_links))
     {
       if (l.objidx == child_idx) links_to_child++;
     }
@@ -578,9 +593,9 @@
     if (parent_idx == clone_idx) parent_idx++;
 
     auto& parent = vertices_[parent_idx];
-    for (unsigned i = 0; i < parent.obj.links.length; i++)
+    for (auto& l : hb_concat (parent.obj.real_links.writer (),
+                              parent.obj.virtual_links.writer ()))
     {
-      auto& l = parent.obj.links[i];
       if (l.objidx != child_idx)
         continue;
 
@@ -601,8 +616,9 @@
     // to invalidate positions. It does not change graph structure so no need
     // to update distances or edge counts.
     auto& parent = vertices_[parent_idx].obj;
-    for (unsigned i = 0; i < parent.links.length; i++)
-      vertices_[parent.links[i].objidx].raise_priority ();
+    for (auto& l : hb_concat (parent.real_links.writer (),
+                              parent.virtual_links.writer ()))
+      vertices_[l.objidx].raise_priority ();
   }
 
   /*
@@ -615,7 +631,8 @@
 
     for (int parent_idx = vertices_.length - 1; parent_idx >= 0; parent_idx--)
     {
-      for (const auto& link : vertices_[parent_idx].obj.links)
+      // Don't need to check virtual links for overflow
+      for (const auto& link : vertices_[parent_idx].obj.real_links)
       {
         int64_t offset = compute_offset (parent_idx, link);
         if (is_valid_offset (offset, link))
@@ -665,11 +682,11 @@
                  "%4d (%4d in, %4d out, space %2d)",
                  o.parent,
                  parent.incoming_edges (),
-                 parent.obj.links.length,
+                 parent.obj.real_links.length + parent.obj.virtual_links.length,
                  space_for (o.parent),
                  o.child,
                  child.incoming_edges (),
-                 child.obj.links.length,
+                 child.obj.real_links.length + child.obj.virtual_links.length,
                  space_for (o.child));
     }
   }
@@ -728,7 +745,8 @@
       if (visited.has (p)) continue;
       visited.add (p);
 
-      for (const auto& l : vertices_[p].obj.links)
+      // Only real links can be wide
+      for (const auto& l : vertices_[p].obj.real_links)
       {
         if (l.objidx == node_idx && l.width == 4 && !l.is_signed)
         {
@@ -755,7 +773,8 @@
 
     for (unsigned p = 0; p < vertices_.length; p++)
     {
-      for (auto& l : vertices_[p].obj.links)
+      for (auto& l : hb_concat (vertices_[p].obj.real_links,
+                                vertices_[p].obj.virtual_links))
       {
         vertices_[l.objidx].parents.push (p);
       }
@@ -823,7 +842,8 @@
       int64_t next_distance = vertices_[next_idx].distance;
       visited[next_idx] = true;
 
-      for (const auto& link : next.obj.links)
+      for (const auto& link : hb_concat (next.obj.real_links,
+                                         next.obj.virtual_links))
       {
         if (visited[link.objidx]) continue;
 
@@ -922,9 +942,9 @@
     if (!id_map) return;
     for (unsigned i : subgraph)
     {
-      for (unsigned j = 0; j < vertices_[i].obj.links.length; j++)
+      for (auto& link : hb_concat (vertices_[i].obj.real_links.writer (),
+                                   vertices_[i].obj.virtual_links.writer ()))
       {
-        auto& link = vertices_[i].obj.links[j];
         if (!id_map.has (link.objidx)) continue;
         if (only_wide && !(link.width == 4 && !link.is_signed)) continue;
 
@@ -942,9 +962,10 @@
     for (unsigned i = 0; i < sorted_graph->length; i++)
     {
       (*sorted_graph)[i].remap_parents (id_map);
-      for (unsigned j = 0; j < (*sorted_graph)[i].obj.links.length; j++)
+      for (auto& link : hb_concat ((*sorted_graph)[i].obj.real_links.writer (),
+                                   (*sorted_graph)[i].obj.virtual_links.writer ()))
+
       {
-        auto& link = (*sorted_graph)[i].obj.links[j];
         link.objidx = id_map[link.objidx];
       }
     }
@@ -1023,7 +1044,8 @@
     const auto& v = vertices_[start_idx];
 
     // Graph is treated as undirected so search children and parents of start_idx
-    for (const auto& l : v.obj.links)
+    for (const auto& l : hb_concat (v.obj.real_links,
+                                    v.obj.virtual_links))
       find_connected_nodes (l.objidx, targets, visited, connected);
 
     for (unsigned p : v.parents)
diff --git a/src/hb-serialize.hh b/src/hb-serialize.hh
index d22ae06..1073901 100644
--- a/src/hb-serialize.hh
+++ b/src/hb-serialize.hh
@@ -65,19 +65,26 @@
 
   struct object_t
   {
-    void fini () { links.fini (); }
+    void fini () {
+      real_links.fini ();
+      virtual_links.fini ();
+    }
 
     bool operator == (const object_t &o) const
     {
+      // Virtual links aren't considered for equality since they don't affect the functionality
+      // of the object.
       return (tail - head == o.tail - o.head)
-	  && (links.length == o.links.length)
+	  && (real_links.length == o.real_links.length)
 	  && 0 == hb_memcmp (head, o.head, tail - head)
-	  && links.as_bytes () == o.links.as_bytes ();
+	  && real_links.as_bytes () == o.real_links.as_bytes ();
     }
     uint32_t hash () const
     {
+      // Virtual links aren't considered for equality since they don't affect the functionality
+      // of the object.
       return hb_bytes_t (head, tail - head).hash () ^
-	     links.as_bytes ().hash ();
+          real_links.as_bytes ().hash ();
     }
 
     struct link_t
@@ -92,7 +99,8 @@
 
     char *head;
     char *tail;
-    hb_vector_t<link_t> links;
+    hb_vector_t<link_t> real_links;
+    hb_vector_t<link_t> virtual_links;
     object_t *next;
   };
 
@@ -101,12 +109,14 @@
     char *head;
     char *tail;
     object_t *current; // Just for sanity check
-    unsigned num_links;
+    unsigned num_real_links;
+    unsigned num_virtual_links;
     hb_serialize_error_t errors;
   };
 
   snapshot_t snapshot ()
-  { return snapshot_t { head, tail, current, current->links.length, errors }; }
+  { return snapshot_t {
+      head, tail, current, current->real_links.length, current->virtual_links.length, errors }; }
 
   hb_serialize_context_t (void *start_, unsigned int size) :
     start ((char *) start_),
@@ -282,7 +292,8 @@
 
     if (!len)
     {
-      assert (!obj->links.length);
+      assert (!obj->real_links.length);
+      assert (!obj->virtual_links.length);
       return 0;
     }
 
@@ -292,6 +303,7 @@
       objidx = packed_map.get (obj);
       if (objidx)
       {
+        merge_virtual_links (obj, objidx);
 	obj->fini ();
 	return objidx;
       }
@@ -327,7 +339,8 @@
     // Overflows that happened after the snapshot will be erased by the revert.
     if (unlikely (in_error () && !only_overflow ())) return;
     assert (snap.current == current);
-    current->links.shrink (snap.num_links);
+    current->real_links.shrink (snap.num_real_links);
+    current->virtual_links.shrink (snap.num_virtual_links);
     errors = snap.errors;
     revert (snap.head, snap.tail);
   }
@@ -375,8 +388,8 @@
 
     assert (current);
 
-    auto& link = *current->links.push ();
-    if (current->links.in_error ())
+    auto& link = *current->virtual_links.push ();
+    if (current->virtual_links.in_error ())
       err (HB_SERIALIZE_ERROR_OTHER);
 
     link.width = 0;
@@ -400,8 +413,8 @@
     assert (current);
     assert (current->head <= (const char *) &ofs);
 
-    auto& link = *current->links.push ();
-    if (current->links.in_error ())
+    auto& link = *current->real_links.push ();
+    if (current->real_links.in_error ())
       err (HB_SERIALIZE_ERROR_OTHER);
 
     link.width = sizeof (T);
@@ -440,10 +453,8 @@
     assert (packed.length > 1);
 
     for (const object_t* parent : ++hb_iter (packed))
-      for (const object_t::link_t &link : parent->links)
+      for (const object_t::link_t &link : parent->real_links)
       {
-        if (unlikely (!link.width)) continue; // Don't need to resolve virtual offsets
-
 	const object_t* child = packed[link.objidx];
 	if (unlikely (!child)) { err (HB_SERIALIZE_ERROR_OTHER); return; }
 	unsigned offset = 0;
@@ -642,6 +653,13 @@
 
   private:
 
+  void merge_virtual_links (const object_t* from, objidx_t to_idx) {
+    object_t* to = packed[to_idx];
+    for (const auto& l : from->virtual_links) {
+      to->virtual_links.push (l);
+    }
+  }
+
   /* Object memory pool. */
   hb_pool_t<object_t> object_pool;
 
diff --git a/src/test-repacker.cc b/src/test-repacker.cc
index 4df0636..86ab2ff 100644
--- a/src/test-repacker.cc
+++ b/src/test-repacker.cc
@@ -763,20 +763,20 @@
 
   start_object ("b", 1, c);
   add_offset (obj_d, c);
-  unsigned obj_b = c->pop_pack ();
+  unsigned obj_b = c->pop_pack (false);
 
   start_object ("e", 1, c);
   add_virtual_offset (obj_b, c);
-  unsigned obj_e = c->pop_pack();
+  unsigned obj_e = c->pop_pack (false);
 
   start_object ("c", 1, c);
   add_offset (obj_e, c);
-  unsigned obj_c = c->pop_pack ();
+  unsigned obj_c = c->pop_pack (false);
 
   start_object ("a", 1, c);
   add_offset (obj_b, c);
   add_offset (obj_c, c);
-  c->pop_pack ();
+  c->pop_pack (false);
 
   c->end_serialize();
 }
@@ -792,19 +792,19 @@
   graph.sort_kahn ();
 
   assert(strncmp (graph.object (3).head, "abc", 3) == 0);
-  assert(graph.object (3).links.length == 2);
-  assert(graph.object (3).links[0].objidx == 2);
-  assert(graph.object (3).links[1].objidx == 1);
+  assert(graph.object (3).real_links.length == 2);
+  assert(graph.object (3).real_links[0].objidx == 2);
+  assert(graph.object (3).real_links[1].objidx == 1);
 
   assert(strncmp (graph.object (2).head, "def", 3) == 0);
-  assert(graph.object (2).links.length == 1);
-  assert(graph.object (2).links[0].objidx == 0);
+  assert(graph.object (2).real_links.length == 1);
+  assert(graph.object (2).real_links[0].objidx == 0);
 
   assert(strncmp (graph.object (1).head, "jkl", 3) == 0);
-  assert(graph.object (1).links.length == 0);
+  assert(graph.object (1).real_links.length == 0);
 
   assert(strncmp (graph.object (0).head, "ghi", 3) == 0);
-  assert(graph.object (0).links.length == 0);
+  assert(graph.object (0).real_links.length == 0);
 
   free (buffer);
 }
@@ -821,24 +821,24 @@
 
 
   assert(strncmp (graph.object (4).head, "abc", 3) == 0);
-  assert(graph.object (4).links.length == 3);
-  assert(graph.object (4).links[0].objidx == 3);
-    assert(graph.object (4).links[1].objidx == 0);
-  assert(graph.object (4).links[2].objidx == 2);
+  assert(graph.object (4).real_links.length == 3);
+  assert(graph.object (4).real_links[0].objidx == 3);
+    assert(graph.object (4).real_links[1].objidx == 0);
+  assert(graph.object (4).real_links[2].objidx == 2);
 
   assert(strncmp (graph.object (3).head, "def", 3) == 0);
-  assert(graph.object (3).links.length == 1);
-  assert(graph.object (3).links[0].objidx == 1);
+  assert(graph.object (3).real_links.length == 1);
+  assert(graph.object (3).real_links[0].objidx == 1);
 
   assert(strncmp (graph.object (2).head, "mn", 2) == 0);
-  assert(graph.object (2).links.length == 0);
+  assert(graph.object (2).real_links.length == 0);
 
   assert(strncmp (graph.object (1).head, "ghi", 3) == 0);
-  assert(graph.object (1).links.length == 1);
-  assert(graph.object (1).links[0].objidx == 0);
+  assert(graph.object (1).real_links.length == 1);
+  assert(graph.object (1).real_links[0].objidx == 0);
 
   assert(strncmp (graph.object (0).head, "jkl", 3) == 0);
-  assert(graph.object (0).links.length == 0);
+  assert(graph.object (0).real_links.length == 0);
 
   free (buffer);
 }
@@ -854,24 +854,24 @@
   graph.sort_shortest_distance ();
 
   assert(strncmp (graph.object (4).head, "abc", 3) == 0);
-  assert(graph.object (4).links.length == 3);
-  assert(graph.object (4).links[0].objidx == 2);
-  assert(graph.object (4).links[1].objidx == 0);
-  assert(graph.object (4).links[2].objidx == 3);
+  assert(graph.object (4).real_links.length == 3);
+  assert(graph.object (4).real_links[0].objidx == 2);
+  assert(graph.object (4).real_links[1].objidx == 0);
+  assert(graph.object (4).real_links[2].objidx == 3);
 
   assert(strncmp (graph.object (3).head, "mn", 2) == 0);
-  assert(graph.object (3).links.length == 0);
+  assert(graph.object (3).real_links.length == 0);
 
   assert(strncmp (graph.object (2).head, "def", 3) == 0);
-  assert(graph.object (2).links.length == 1);
-  assert(graph.object (2).links[0].objidx == 1);
+  assert(graph.object (2).real_links.length == 1);
+  assert(graph.object (2).real_links[0].objidx == 1);
 
   assert(strncmp (graph.object (1).head, "ghi", 3) == 0);
-  assert(graph.object (1).links.length == 1);
-  assert(graph.object (1).links[0].objidx == 0);
+  assert(graph.object (1).real_links.length == 1);
+  assert(graph.object (1).real_links[0].objidx == 0);
 
   assert(strncmp (graph.object (0).head, "jkl", 3) == 0);
-  assert(graph.object (0).links.length == 0);
+  assert(graph.object (0).real_links.length == 0);
 
   free (buffer);
 }
@@ -887,27 +887,27 @@
   graph.duplicate (4, 1);
 
   assert(strncmp (graph.object (5).head, "abc", 3) == 0);
-  assert(graph.object (5).links.length == 3);
-  assert(graph.object (5).links[0].objidx == 3);
-  assert(graph.object (5).links[1].objidx == 4);
-  assert(graph.object (5).links[2].objidx == 0);
+  assert(graph.object (5).real_links.length == 3);
+  assert(graph.object (5).real_links[0].objidx == 3);
+  assert(graph.object (5).real_links[1].objidx == 4);
+  assert(graph.object (5).real_links[2].objidx == 0);
 
   assert(strncmp (graph.object (4).head, "jkl", 3) == 0);
-  assert(graph.object (4).links.length == 0);
+  assert(graph.object (4).real_links.length == 0);
 
   assert(strncmp (graph.object (3).head, "def", 3) == 0);
-  assert(graph.object (3).links.length == 1);
-  assert(graph.object (3).links[0].objidx == 2);
+  assert(graph.object (3).real_links.length == 1);
+  assert(graph.object (3).real_links[0].objidx == 2);
 
   assert(strncmp (graph.object (2).head, "ghi", 3) == 0);
-  assert(graph.object (2).links.length == 1);
-  assert(graph.object (2).links[0].objidx == 1);
+  assert(graph.object (2).real_links.length == 1);
+  assert(graph.object (2).real_links[0].objidx == 1);
 
   assert(strncmp (graph.object (1).head, "jkl", 3) == 0);
-  assert(graph.object (1).links.length == 0);
+  assert(graph.object (1).real_links.length == 0);
 
   assert(strncmp (graph.object (0).head, "mn", 2) == 0);
-  assert(graph.object (0).links.length == 0);
+  assert(graph.object (0).real_links.length == 0);
 
   free (buffer);
 }
@@ -923,32 +923,32 @@
   graph.duplicate (3, 2);
 
   assert(strncmp (graph.object (6).head, "abc", 3) == 0);
-  assert(graph.object (6).links.length == 3);
-  assert(graph.object (6).links[0].objidx == 4);
-  assert(graph.object (6).links[1].objidx == 2);
-  assert(graph.object (6).links[2].objidx == 1);
+  assert(graph.object (6).real_links.length == 3);
+  assert(graph.object (6).real_links[0].objidx == 4);
+  assert(graph.object (6).real_links[1].objidx == 2);
+  assert(graph.object (6).real_links[2].objidx == 1);
 
   assert(strncmp (graph.object (5).head, "jkl", 3) == 0);
-  assert(graph.object (5).links.length == 1);
-  assert(graph.object (5).links[0].objidx == 0);
+  assert(graph.object (5).real_links.length == 1);
+  assert(graph.object (5).real_links[0].objidx == 0);
 
   assert(strncmp (graph.object (4).head, "def", 3) == 0);
-  assert(graph.object (4).links.length == 1);
-  assert(graph.object (4).links[0].objidx == 3);
+  assert(graph.object (4).real_links.length == 1);
+  assert(graph.object (4).real_links[0].objidx == 3);
 
   assert(strncmp (graph.object (3).head, "ghi", 3) == 0);
-  assert(graph.object (3).links.length == 1);
-  assert(graph.object (3).links[0].objidx == 5);
+  assert(graph.object (3).real_links.length == 1);
+  assert(graph.object (3).real_links[0].objidx == 5);
 
   assert(strncmp (graph.object (2).head, "jkl", 3) == 0);
-  assert(graph.object (2).links.length == 1);
-  assert(graph.object (2).links[0].objidx == 0);
+  assert(graph.object (2).real_links.length == 1);
+  assert(graph.object (2).real_links[0].objidx == 0);
 
   assert(strncmp (graph.object (1).head, "mn", 2) == 0);
-  assert(graph.object (1).links.length == 0);
+  assert(graph.object (1).real_links.length == 0);
 
   assert(strncmp (graph.object (0).head, "opqrst", 6) == 0);
-  assert(graph.object (0).links.length == 0);
+  assert(graph.object (0).real_links.length == 0);
 
   free (buffer);
 }
@@ -1247,6 +1247,43 @@
   free (out_buffer);
 }
 
+static void
+test_shared_node_with_virtual_links ()
+{
+  size_t buffer_size = 100;
+  void* buffer = malloc (buffer_size);
+  hb_serialize_context_t c (buffer, buffer_size);
+
+  c.start_serialize<char> ();
+
+  unsigned obj_b = add_object ("b", 1, &c);
+  unsigned obj_c = add_object ("c", 1, &c);
+
+  start_object ("d", 1, &c);
+  add_virtual_offset (obj_b, &c);
+  unsigned obj_d_1 = c.pop_pack ();
+
+  start_object ("d", 1, &c);
+  add_virtual_offset (obj_c, &c);
+  unsigned obj_d_2 = c.pop_pack ();
+
+  assert (obj_d_1 == obj_d_2);
+
+  start_object ("a", 1, &c);
+  add_offset (obj_b, &c);
+  add_offset (obj_c, &c);
+  add_offset (obj_d_1, &c);
+  add_offset (obj_d_2, &c);
+  c.pop_pack ();
+  c.end_serialize ();
+
+  assert(c.object_graph() [obj_d_1]->virtual_links.length == 2);
+  assert(c.object_graph() [obj_d_1]->virtual_links[0].objidx == obj_b);
+  assert(c.object_graph() [obj_d_1]->virtual_links[1].objidx == obj_c);
+  free(buffer);
+}
+
+
 // TODO(garretrieger): update will_overflow tests to check the overflows array.
 // TODO(garretrieger): add tests for priority raising.
 
@@ -1273,4 +1310,5 @@
   test_duplicate_leaf ();
   test_duplicate_interior ();
   test_virtual_link ();
+  test_shared_node_with_virtual_links ();
 }
diff --git a/test/subset/data/expected/layout.default_features/FranklinGothic-Regular.layout-test.retain-all-codepoint.ttf b/test/subset/data/expected/layout.default_features/FranklinGothic-Regular.layout-test.retain-all-codepoint.ttf
index 81104ce..c341ab8 100644
--- a/test/subset/data/expected/layout.default_features/FranklinGothic-Regular.layout-test.retain-all-codepoint.ttf
+++ b/test/subset/data/expected/layout.default_features/FranklinGothic-Regular.layout-test.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.gdef.glyphset/IndicTestHowrah-Regular.default.retain-all-codepoint.ttf b/test/subset/data/expected/layout.gdef.glyphset/IndicTestHowrah-Regular.default.retain-all-codepoint.ttf
index 0f1c180..615474b 100644
--- a/test/subset/data/expected/layout.gdef.glyphset/IndicTestHowrah-Regular.default.retain-all-codepoint.ttf
+++ b/test/subset/data/expected/layout.gdef.glyphset/IndicTestHowrah-Regular.default.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.gdef.glyphset/IndicTestHowrah-Regular.drop-hints.retain-all-codepoint.ttf b/test/subset/data/expected/layout.gdef.glyphset/IndicTestHowrah-Regular.drop-hints.retain-all-codepoint.ttf
index 148b318..47ae513 100644
--- a/test/subset/data/expected/layout.gdef.glyphset/IndicTestHowrah-Regular.drop-hints.retain-all-codepoint.ttf
+++ b/test/subset/data/expected/layout.gdef.glyphset/IndicTestHowrah-Regular.drop-hints.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.gdef.glyphset/IndicTestHowrah-Regular.retain-gids.retain-all-codepoint.ttf b/test/subset/data/expected/layout.gdef.glyphset/IndicTestHowrah-Regular.retain-gids.retain-all-codepoint.ttf
index 3b1f301..240cb05 100644
--- a/test/subset/data/expected/layout.gdef.glyphset/IndicTestHowrah-Regular.retain-gids.retain-all-codepoint.ttf
+++ b/test/subset/data/expected/layout.gdef.glyphset/IndicTestHowrah-Regular.retain-gids.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.default.627,644,62D,628.ttf b/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.default.627,644,62D,628.ttf
index bf2ff01..b023425 100644
--- a/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.default.627,644,62D,628.ttf
+++ b/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.default.627,644,62D,628.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.default.63A,64A,631.ttf b/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.default.63A,64A,631.ttf
index fdd0b84..4816fa7 100644
--- a/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.default.63A,64A,631.ttf
+++ b/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.default.63A,64A,631.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.retain-gids.627,644,62D,628.ttf b/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.retain-gids.627,644,62D,628.ttf
index 64d8565..2fb3f15 100644
--- a/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.retain-gids.627,644,62D,628.ttf
+++ b/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.retain-gids.627,644,62D,628.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.retain-gids.63A,64A,631.ttf b/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.retain-gids.63A,64A,631.ttf
index d69ba99..0b15e51 100644
--- a/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.retain-gids.63A,64A,631.ttf
+++ b/test/subset/data/expected/layout.notonastaliqurdu/NotoNastaliqUrdu-Bold.retain-gids.63A,64A,631.ttf
Binary files differ
diff --git a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.default.c30,c36,c40,c4d.ttf b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.default.c30,c36,c40,c4d.ttf
index f96e74f..89bbc99 100644
--- a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.default.c30,c36,c40,c4d.ttf
+++ b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.default.c30,c36,c40,c4d.ttf
Binary files differ
diff --git a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.default.retain-all-codepoint.ttf b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.default.retain-all-codepoint.ttf
index dcbcd3e..6d4ea77 100644
--- a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.default.retain-all-codepoint.ttf
+++ b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.default.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.glyph-names.c30,c36,c40,c4d.ttf b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.glyph-names.c30,c36,c40,c4d.ttf
index 984fde5..48ca1c5 100644
--- a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.glyph-names.c30,c36,c40,c4d.ttf
+++ b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.glyph-names.c30,c36,c40,c4d.ttf
Binary files differ
diff --git a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.glyph-names.retain-all-codepoint.ttf b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.glyph-names.retain-all-codepoint.ttf
index 2db21f8..d96dd36 100644
--- a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.glyph-names.retain-all-codepoint.ttf
+++ b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.glyph-names.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.notdef-outline.c30,c36,c40,c4d.ttf b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.notdef-outline.c30,c36,c40,c4d.ttf
index 882fc57..ae3f659 100644
--- a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.notdef-outline.c30,c36,c40,c4d.ttf
+++ b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.notdef-outline.c30,c36,c40,c4d.ttf
Binary files differ
diff --git a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.notdef-outline.retain-all-codepoint.ttf b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.notdef-outline.retain-all-codepoint.ttf
index 193afd0..3163b3c 100644
--- a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.notdef-outline.retain-all-codepoint.ttf
+++ b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.notdef-outline.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.retain-gids.c30,c36,c40,c4d.ttf b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.retain-gids.c30,c36,c40,c4d.ttf
index 99419f9..7b15499 100644
--- a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.retain-gids.c30,c36,c40,c4d.ttf
+++ b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.retain-gids.c30,c36,c40,c4d.ttf
Binary files differ
diff --git a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.retain-gids.retain-all-codepoint.ttf b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.retain-gids.retain-all-codepoint.ttf
index 9cf9c25..3927219 100644
--- a/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.retain-gids.retain-all-codepoint.ttf
+++ b/test/subset/data/expected/post/SreeKrushnadevaraya-Regular.retain-gids.retain-all-codepoint.ttf
Binary files differ