[PECOFF] Support yet another new type of weak symbol.

COMDAT_SELECT_LARGEST is a COMDAT type that make linker to choose the largest
definition from among all of the definition of a symbol. If the size is the
same, the choice is arbitrary.

Differential Revision: http://llvm-reviews.chandlerc.com/D3011

llvm-svn: 204172
diff --git a/lld/lib/Core/SymbolTable.cpp b/lld/lib/Core/SymbolTable.cpp
index add6628..9bacdad 100644
--- a/lld/lib/Core/SymbolTable.cpp
+++ b/lld/lib/Core/SymbolTable.cpp
@@ -102,13 +102,14 @@
   MCR_Error
 };
 
-static MergeResolution mergeCases[][5] = {
-  // no          tentative      weak          weakAddress   sameNameAndSize
-  {MCR_Error,    MCR_First,     MCR_First,    MCR_First,    MCR_SameSize}, // no
-  {MCR_Second,   MCR_Largest,   MCR_Second,   MCR_Second,   MCR_SameSize}, // tentative
-  {MCR_Second,   MCR_First,     MCR_First,    MCR_Second,   MCR_SameSize}, // weak
-  {MCR_Second,   MCR_First,     MCR_First,    MCR_First,    MCR_SameSize}, // weakAddress
-  {MCR_SameSize, MCR_SameSize,  MCR_SameSize, MCR_SameSize, MCR_SameSize}, // sameSize
+static MergeResolution mergeCases[][6] = {
+  // no          tentative      weak          weakAddress   sameNameAndSize largest
+  {MCR_Error,    MCR_First,     MCR_First,    MCR_First,    MCR_SameSize,   MCR_Largest},  // no
+  {MCR_Second,   MCR_Largest,   MCR_Second,   MCR_Second,   MCR_SameSize,   MCR_Largest},  // tentative
+  {MCR_Second,   MCR_First,     MCR_First,    MCR_Second,   MCR_SameSize,   MCR_Largest},  // weak
+  {MCR_Second,   MCR_First,     MCR_First,    MCR_First,    MCR_SameSize,   MCR_Largest},  // weakAddress
+  {MCR_SameSize, MCR_SameSize,  MCR_SameSize, MCR_SameSize, MCR_SameSize,   MCR_SameSize}, // sameSize
+  {MCR_Largest,  MCR_Largest,   MCR_Largest,  MCR_Largest,  MCR_SameSize,   MCR_Largest},  // largest
 };
 
 static MergeResolution mergeSelect(DefinedAtom::Merge first,
@@ -118,6 +119,33 @@
   return mergeCases[first][second];
 }
 
+static uint64_t getSizeFollowReferences(const DefinedAtom *atom, uint32_t kind) {
+  uint64_t size = 0;
+redo:
+  while (atom) {
+    for (const Reference *r : *atom) {
+      if (r->kindNamespace() == Reference::KindNamespace::all &&
+          r->kindArch() == Reference::KindArch::all &&
+          r->kindValue() == kind) {
+        atom = cast<DefinedAtom>(r->target());
+        size += atom->size();
+        goto redo;
+      }
+    }
+    break;
+  }
+  return size;
+}
+
+// Returns the size of the section containing the given atom. Atoms in the same
+// section are connected by layout-before and layout-after edges, so this
+// function traverses them to get the total size of atoms in the same section.
+static uint64_t sectionSize(const DefinedAtom *atom) {
+  return atom->size()
+      + getSizeFollowReferences(atom, lld::Reference::kindLayoutBefore)
+      + getSizeFollowReferences(atom, lld::Reference::kindLayoutAfter);
+}
+
 void SymbolTable::addByName(const Atom &newAtom) {
   StringRef name = newAtom.name();
   assert(!name.empty());
@@ -148,19 +176,22 @@
     case MCR_Second:
       useNew = true;
       break;
-    case MCR_Largest:
-      useNew = true;
+    case MCR_Largest: {
+      uint64_t existingSize = sectionSize((DefinedAtom*)existing);
+      uint64_t newSize = sectionSize((DefinedAtom*)&newAtom);
+      useNew = (newSize >= existingSize);
       break;
+    }
     case MCR_SameSize: {
-      uint64_t sa = ((DefinedAtom*)existing)->size();
-      uint64_t sb = ((DefinedAtom*)&newAtom)->size();
-      if (sa == sb) {
+      uint64_t existingSize = sectionSize((DefinedAtom*)existing);
+      uint64_t newSize = sectionSize((DefinedAtom*)&newAtom);
+      if (existingSize == newSize) {
         useNew = true;
         break;
       }
       llvm::errs() << "Size mismatch: "
-                   << existing->name() << " (" << sa << ") "
-                   << newAtom.name() << " (" << sb << ")\n";
+                   << existing->name() << " (" << existingSize << ") "
+                   << newAtom.name() << " (" << newSize << ")\n";
       // fallthrough
     }
     case MCR_Error: