[BOLT] Support multiple parents for split jump table
authorHuan Nguyen <nhuhuan@yahoo.com>
Thu, 14 Jul 2022 06:35:51 +0000 (23:35 -0700)
committerHuan Nguyen <nhuhuan@yahoo.com>
Thu, 14 Jul 2022 06:37:31 +0000 (23:37 -0700)
There are two assumptions regarding jump table:
(a) It is accessed by only one fragment, say, Parent
(b) All entries target instructions in Parent

For (a), BOLT stores jump table entries as relative offset to Parent.
For (b), BOLT treats jump table entries target somewhere out of Parent
as INVALID_OFFSET, including fragment of same split function.

In this update, we extend (a) and (b) to include fragment of same split
functinon. For (a), we store jump table entries in absolute offset
instead. In addition, jump table will store all fragments that access
it. A fragment uses this information to only create label for jump table
entries that target to that fragment.

For (b), using absolute offset allows jump table entries to target
fragments of same split function, i.e., extend support for split jump
table. This can be done using relocation (fragment start/size) and
fragment detection heuristics (e.g., using symbol name pattern for
non-stripped binaries).

For jump table targets that can only be reached by one fragment, we
mark them as local label; otherwise, they would be the secondary
function entry to the target fragment.

Test Plan
```
ninja check-bolt
```

Reviewed By: Amir

Differential Revision: https://reviews.llvm.org/D128474

bolt/include/bolt/Core/BinaryContext.h
bolt/include/bolt/Core/BinaryFunction.h
bolt/include/bolt/Core/JumpTable.h
bolt/lib/Core/BinaryContext.cpp
bolt/lib/Core/BinaryFunction.cpp
bolt/lib/Core/JumpTable.cpp
bolt/lib/Passes/AsmDump.cpp
bolt/lib/Rewrite/RewriteInstance.cpp
bolt/test/X86/jump-table-move.s [new file with mode: 0644]
bolt/test/X86/split-func-jump-table-fragment-bidirection.s

index f2fb727..8d385e9 100644 (file)
@@ -200,7 +200,7 @@ class BinaryContext {
   uint32_t DuplicatedJumpTables{0x10000000};
 
   /// Function fragments to skip.
-  std::vector<BinaryFunction *> FragmentsToSkip;
+  std::unordered_set<BinaryFunction *> FragmentsToSkip;
 
   /// The runtime library.
   std::unique_ptr<RuntimeLibrary> RtLibrary;
@@ -236,6 +236,18 @@ public:
     MIB = std::move(TargetBuilder);
   }
 
+  /// Return function fragments to skip.
+  const std::unordered_set<BinaryFunction *> &getFragmentsToSkip() {
+    return FragmentsToSkip;
+  }
+
+  /// Add function fragment to skip
+  void addFragmentsToSkip(BinaryFunction *Function) {
+    FragmentsToSkip.insert(Function);
+  }
+
+  void clearFragmentsToSkip() { FragmentsToSkip.clear(); }
+
   /// Given DWOId returns CU if it exists in DWOCUs.
   Optional<DWARFUnit *> getDWOCU(uint64_t DWOId);
 
@@ -476,15 +488,15 @@ public:
   /// If \p NextJTAddress is different from zero, it is used as an upper
   /// bound for jump table memory layout.
   ///
-  /// Optionally, populate \p Offsets with jump table entries. The entries
+  /// Optionally, populate \p Address from jump table entries. The entries
   /// could be partially populated if the jump table detection fails.
   bool analyzeJumpTable(const uint64_t Address,
                         const JumpTable::JumpTableType Type, BinaryFunction &BF,
                         const uint64_t NextJTAddress = 0,
-                        JumpTable::OffsetsType *Offsets = nullptr);
+                        JumpTable::AddressesType *EntriesAsAddress = nullptr);
 
   /// After jump table locations are established, this function will populate
-  /// their OffsetEntries based on memory contents.
+  /// their EntriesAsAddress based on memory contents.
   void populateJumpTables();
 
   /// Returns a jump table ID and label pointing to the duplicated jump table.
@@ -499,12 +511,12 @@ public:
   /// to function \p BF.
   std::string generateJumpTableName(const BinaryFunction &BF, uint64_t Address);
 
-  /// Free memory used by jump table offsets
-  void clearJumpTableOffsets() {
+  /// Free memory used by JumpTable's EntriesAsAddress
+  void clearJumpTableTempData() {
     for (auto &JTI : JumpTables) {
       JumpTable &JT = *JTI.second;
-      JumpTable::OffsetsType Temp;
-      Temp.swap(JT.OffsetEntries);
+      JumpTable::AddressesType Temp;
+      Temp.swap(JT.EntriesAsAddress);
     }
   }
   /// Return true if the array of bytes represents a valid code padding.
index 2bba810..43ae333 100644 (file)
@@ -333,9 +333,9 @@ private:
   /// True if the original entry point was patched.
   bool IsPatched{false};
 
-  /// True if the function contains jump table with entries pointing to
-  /// locations in fragments.
-  bool HasSplitJumpTable{false};
+  /// True if the function contains explicit or implicit indirect branch to its
+  /// split fragments, e.g., split jump table, landing pad in split fragment
+  bool HasIndirectTargetToSplitFragment{false};
 
   /// True if there are no control-flow edges with successors in other functions
   /// (i.e. if tail calls have edges to function-local basic blocks).
@@ -1437,9 +1437,12 @@ public:
   /// otherwise processed.
   bool isPseudo() const { return IsPseudo; }
 
-  /// Return true if the function contains a jump table with entries pointing
-  /// to split fragments.
-  bool hasSplitJumpTable() const { return HasSplitJumpTable; }
+  /// Return true if the function contains explicit or implicit indirect branch
+  /// to its split fragments, e.g., split jump table, landing pad in split
+  /// fragment.
+  bool hasIndirectTargetToSplitFragment() const {
+    return HasIndirectTargetToSplitFragment;
+  }
 
   /// Return true if all CFG edges have local successors.
   bool hasCanonicalCFG() const { return HasCanonicalCFG; }
@@ -1834,7 +1837,9 @@ public:
 
   void setIsPatched(bool V) { IsPatched = V; }
 
-  void setHasSplitJumpTable(bool V) { HasSplitJumpTable = V; }
+  void setHasIndirectTargetToSplitFragment(bool V) {
+    HasIndirectTargetToSplitFragment = V;
+  }
 
   void setHasCanonicalCFG(bool V) { HasCanonicalCFG = V; }
 
index e5f031e..9615025 100644 (file)
@@ -69,9 +69,9 @@ public:
   /// All the entries as labels.
   std::vector<MCSymbol *> Entries;
 
-  /// All the entries as offsets into a function. Invalid after CFG is built.
-  using OffsetsType = std::vector<uint64_t>;
-  OffsetsType OffsetEntries;
+  /// All the entries as absolute addresses. Invalid after disassembly is done.
+  using AddressesType = std::vector<uint64_t>;
+  AddressesType EntriesAsAddress;
 
   /// Map <Offset> -> <Label> used for embedded jump tables. Label at 0 offset
   /// is the main label for the jump table.
@@ -87,18 +87,17 @@ public:
   uint64_t Count{0};
 
   /// BinaryFunction this jump tables belongs to.
-  BinaryFunction *Parent{nullptr};
+  SmallVector<BinaryFunction *, 1> Parents;
 
 private:
   /// Constructor should only be called by a BinaryContext.
   JumpTable(MCSymbol &Symbol, uint64_t Address, size_t EntrySize,
-            JumpTableType Type, LabelMapType &&Labels, BinaryFunction &BF,
-            BinarySection &Section);
+            JumpTableType Type, LabelMapType &&Labels, BinarySection &Section);
 
 public:
   /// Return the size of the jump table.
   uint64_t getSize() const {
-    return std::max(OffsetEntries.size(), Entries.size()) * EntrySize;
+    return std::max(EntriesAsAddress.size(), Entries.size()) * EntrySize;
   }
 
   const MCSymbol *getFirstLabel() const {
index 0f60694..9331643 100644 (file)
@@ -490,21 +490,19 @@ bool isPotentialFragmentByName(BinaryFunction &Fragment,
   return false;
 }
 
-bool BinaryContext::analyzeJumpTable(const uint64_t Address,
-                                     const JumpTable::JumpTableType Type,
-                                     BinaryFunction &BF,
-                                     const uint64_t NextJTAddress,
-                                     JumpTable::OffsetsType *Offsets) {
+bool BinaryContext::analyzeJumpTable(
+    const uint64_t Address, const JumpTable::JumpTableType Type,
+    BinaryFunction &BF, const uint64_t NextJTAddress,
+    JumpTable::AddressesType *EntriesAsAddress) {
   // Is one of the targets __builtin_unreachable?
   bool HasUnreachable = false;
 
   // Number of targets other than __builtin_unreachable.
   uint64_t NumRealEntries = 0;
 
-  constexpr uint64_t INVALID_OFFSET = std::numeric_limits<uint64_t>::max();
-  auto addOffset = [&](uint64_t Offset) {
-    if (Offsets)
-      Offsets->emplace_back(Offset);
+  auto addEntryAddress = [&](uint64_t EntryAddress) {
+    if (EntriesAsAddress)
+      EntriesAsAddress->emplace_back(EntryAddress);
   };
 
   auto doesBelongToFunction = [&](const uint64_t Addr,
@@ -570,7 +568,7 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
 
     // __builtin_unreachable() case.
     if (Value == BF.getAddress() + BF.getSize()) {
-      addOffset(Value - BF.getAddress());
+      addEntryAddress(Value);
       HasUnreachable = true;
       LLVM_DEBUG(dbgs() << "OK: __builtin_unreachable\n");
       continue;
@@ -610,18 +608,9 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
 
     ++NumRealEntries;
 
-    if (TargetBF == &BF) {
-      // Address inside the function.
-      addOffset(Value - TargetBF->getAddress());
-      LLVM_DEBUG(dbgs() << "OK: real entry\n");
-    } else {
-      // Address in split fragment.
-      BF.setHasSplitJumpTable(true);
-      // Add invalid offset for proper identification of jump table size.
-      addOffset(INVALID_OFFSET);
-      LLVM_DEBUG(dbgs() << "OK: address in split fragment "
-                        << TargetBF->getPrintName() << '\n');
-    }
+    if (TargetBF != &BF)
+      BF.setHasIndirectTargetToSplitFragment(true);
+    addEntryAddress(Value);
   }
 
   // It's a jump table if the number of real entries is more than 1, or there's
@@ -636,9 +625,11 @@ void BinaryContext::populateJumpTables() {
   for (auto JTI = JumpTables.begin(), JTE = JumpTables.end(); JTI != JTE;
        ++JTI) {
     JumpTable *JT = JTI->second;
-    BinaryFunction &BF = *JT->Parent;
 
-    if (!BF.isSimple())
+    bool NonSimpleParent = false;
+    for (BinaryFunction *BF : JT->Parents)
+      NonSimpleParent |= !BF->isSimple();
+    if (NonSimpleParent)
       continue;
 
     uint64_t NextJTAddress = 0;
@@ -646,25 +637,40 @@ void BinaryContext::populateJumpTables() {
     if (NextJTI != JTE)
       NextJTAddress = NextJTI->second->getAddress();
 
-    const bool Success = analyzeJumpTable(JT->getAddress(), JT->Type, BF,
-                                          NextJTAddress, &JT->OffsetEntries);
+    const bool Success =
+        analyzeJumpTable(JT->getAddress(), JT->Type, *(JT->Parents[0]),
+                         NextJTAddress, &JT->EntriesAsAddress);
     if (!Success) {
-      dbgs() << "failed to analyze jump table in function " << BF << '\n';
+      LLVM_DEBUG(ListSeparator LS;
+                 dbgs() << "failed to analyze jump table in function ";
+                 for (BinaryFunction *Frag
+                      : JT->Parents) dbgs()
+                 << LS << *Frag;
+                 dbgs() << '\n';);
       JT->print(dbgs());
       if (NextJTI != JTE) {
-        dbgs() << "next jump table at 0x"
-               << Twine::utohexstr(NextJTI->second->getAddress())
-               << " belongs to function " << *NextJTI->second->Parent << '\n';
+        LLVM_DEBUG(ListSeparator LS;
+                   dbgs() << "next jump table at 0x"
+                          << Twine::utohexstr(NextJTI->second->getAddress())
+                          << " belongs to function ";
+                   for (BinaryFunction *Frag
+                        : NextJTI->second->Parents) dbgs()
+                   << LS << *Frag;
+                   dbgs() << "\n";);
         NextJTI->second->print(dbgs());
       }
       llvm_unreachable("jump table heuristic failure");
     }
-
-    for (uint64_t EntryOffset : JT->OffsetEntries) {
-      if (EntryOffset == BF.getSize())
-        BF.IgnoredBranches.emplace_back(EntryOffset, BF.getSize());
-      else
-        BF.registerReferencedOffset(EntryOffset);
+    for (BinaryFunction *Frag : JT->Parents) {
+      for (uint64_t EntryAddress : JT->EntriesAsAddress)
+        // if target is builtin_unreachable
+        if (EntryAddress == Frag->getAddress() + Frag->getSize()) {
+          Frag->IgnoredBranches.emplace_back(EntryAddress - Frag->getAddress(),
+                                             Frag->getSize());
+        } else if (EntryAddress >= Frag->getAddress() &&
+                   EntryAddress < Frag->getAddress() + Frag->getSize()) {
+          Frag->registerReferencedOffset(EntryAddress - Frag->getAddress());
+        }
     }
 
     // In strict mode, erase PC-relative relocation record. Later we check that
@@ -678,8 +684,9 @@ void BinaryContext::populateJumpTables() {
     }
 
     // Mark to skip the function and all its fragments.
-    if (BF.hasSplitJumpTable())
-      FragmentsToSkip.push_back(&BF);
+    for (BinaryFunction *Frag : JT->Parents)
+      if (Frag->hasIndirectTargetToSplitFragment())
+        addFragmentsToSkip(Frag);
   }
 
   if (opts::StrictMode && DataPCRelocations.size()) {
@@ -695,27 +702,25 @@ void BinaryContext::populateJumpTables() {
 }
 
 void BinaryContext::skipMarkedFragments() {
-  // Unique functions in the vector.
-  std::unordered_set<BinaryFunction *> UniqueFunctions(FragmentsToSkip.begin(),
-                                                       FragmentsToSkip.end());
-  // Copy the functions back to FragmentsToSkip.
-  FragmentsToSkip.assign(UniqueFunctions.begin(), UniqueFunctions.end());
+  std::vector<BinaryFunction *> FragmentQueue;
+  // Copy the functions to FragmentQueue.
+  FragmentQueue.assign(FragmentsToSkip.begin(), FragmentsToSkip.end());
   auto addToWorklist = [&](BinaryFunction *Function) -> void {
-    if (UniqueFunctions.count(Function))
+    if (FragmentsToSkip.count(Function))
       return;
-    FragmentsToSkip.push_back(Function);
-    UniqueFunctions.insert(Function);
+    FragmentQueue.push_back(Function);
+    addFragmentsToSkip(Function);
   };
   // Functions containing split jump tables need to be skipped with all
   // fragments (transitively).
-  for (size_t I = 0; I != FragmentsToSkip.size(); I++) {
-    BinaryFunction *BF = FragmentsToSkip[I];
-    assert(UniqueFunctions.count(BF) &&
+  for (size_t I = 0; I != FragmentQueue.size(); I++) {
+    BinaryFunction *BF = FragmentQueue[I];
+    assert(FragmentsToSkip.count(BF) &&
            "internal error in traversing function fragments");
     if (opts::Verbosity >= 1)
       errs() << "BOLT-WARNING: Ignoring " << BF->getPrintName() << '\n';
     BF->setSimple(false);
-    BF->setHasSplitJumpTable(true);
+    BF->setHasIndirectTargetToSplitFragment(true);
 
     llvm::for_each(BF->Fragments, addToWorklist);
     llvm::for_each(BF->ParentFragments, addToWorklist);
@@ -724,7 +729,6 @@ void BinaryContext::skipMarkedFragments() {
     errs() << "BOLT-WARNING: skipped " << FragmentsToSkip.size() << " function"
            << (FragmentsToSkip.size() == 1 ? "" : "s")
            << " due to cold fragments\n";
-  FragmentsToSkip.clear();
 }
 
 MCSymbol *BinaryContext::getOrCreateGlobalSymbol(uint64_t Address, Twine Prefix,
@@ -766,27 +770,37 @@ BinaryContext::getOrCreateJumpTable(BinaryFunction &Function, uint64_t Address,
     return (Fragment->isFragment() && Fragment->isParentFragment(Parent));
   };
 
+  // Two fragments of same function access same jump table
   if (JumpTable *JT = getJumpTableContainingAddress(Address)) {
     assert(JT->Type == Type && "jump table types have to match");
-    bool HasMultipleParents = isFragmentOf(JT->Parent, &Function) ||
-                              isFragmentOf(&Function, JT->Parent);
-    assert((JT->Parent == &Function || HasMultipleParents) &&
-           "cannot re-use jump table of a different function");
     assert(Address == JT->getAddress() && "unexpected non-empty jump table");
 
-    // Flush OffsetEntries with INVALID_OFFSET if multiple parents
-    // Duplicate the entry for the parent function for easy access
-    if (HasMultipleParents) {
+    // Prevent associating a jump table to a specific fragment twice.
+    // This simple check arises from the assumption: no more than 2 fragments.
+    if (JT->Parents.size() == 1 && JT->Parents[0] != &Function) {
+      bool SameFunction = isFragmentOf(JT->Parents[0], &Function) ||
+                          isFragmentOf(&Function, JT->Parents[0]);
+      assert(SameFunction &&
+             "cannot re-use jump table of a different function");
+      // Duplicate the entry for the parent function for easy access
+      JT->Parents.push_back(&Function);
       if (opts::Verbosity > 2) {
-        outs() << "BOLT-WARNING: Multiple fragments access same jump table: "
-               << JT->Parent->getPrintName() << "; " << Function.getPrintName()
-               << "\n";
+        outs() << "BOLT-INFO: Multiple fragments access same jump table: "
+               << JT->Parents[0]->getPrintName() << "; "
+               << Function.getPrintName() << "\n";
+        JT->print(outs());
       }
-      constexpr uint64_t INVALID_OFFSET = std::numeric_limits<uint64_t>::max();
-      for (unsigned I = 0; I < JT->OffsetEntries.size(); ++I)
-        JT->OffsetEntries[I] = INVALID_OFFSET;
       Function.JumpTables.emplace(Address, JT);
+      JT->Parents[0]->setHasIndirectTargetToSplitFragment(true);
+      JT->Parents[1]->setHasIndirectTargetToSplitFragment(true);
     }
+
+    bool IsJumpTableParent = false;
+    for (BinaryFunction *Frag : JT->Parents)
+      if (Frag == &Function)
+        IsJumpTableParent = true;
+    assert(IsJumpTableParent &&
+           "cannot re-use jump table of a different function");
     return JT->getFirstLabel();
   }
 
@@ -807,13 +821,15 @@ BinaryContext::getOrCreateJumpTable(BinaryFunction &Function, uint64_t Address,
                     << " in function " << Function << '\n');
 
   JumpTable *JT = new JumpTable(*JTLabel, Address, EntrySize, Type,
-                                JumpTable::LabelMapType{{0, JTLabel}}, Function,
+                                JumpTable::LabelMapType{{0, JTLabel}},
                                 *getSectionForAddress(Address));
+  JT->Parents.push_back(&Function);
+  if (opts::Verbosity > 2)
+    JT->print(outs());
   JumpTables.emplace(Address, JT);
 
   // Duplicate the entry for the parent function for easy access.
   Function.JumpTables.emplace(Address, JT);
-
   return JTLabel;
 }
 
@@ -835,8 +851,9 @@ BinaryContext::duplicateJumpTable(BinaryFunction &Function, JumpTable *JT,
   MCSymbol *NewLabel = Ctx->createNamedTempSymbol("duplicatedJT");
   JumpTable *NewJT =
       new JumpTable(*NewLabel, JT->getAddress(), JT->EntrySize, JT->Type,
-                    JumpTable::LabelMapType{{Offset, NewLabel}}, Function,
+                    JumpTable::LabelMapType{{Offset, NewLabel}},
                     *getSectionForAddress(JT->getAddress()));
+  NewJT->Parents = JT->Parents;
   NewJT->Entries = JT->Entries;
   NewJT->Counts = JT->Counts;
   uint64_t JumpTableID = ++DuplicatedJumpTables;
index 5fdb1b4..f47abbc 100644 (file)
@@ -1642,11 +1642,35 @@ void BinaryFunction::postProcessJumpTables() {
              << *this << '\n';
     }
     if (JT.Entries.empty()) {
-      for (unsigned I = 0; I < JT.OffsetEntries.size(); ++I) {
-        MCSymbol *Label =
-            getOrCreateLocalLabel(getAddress() + JT.OffsetEntries[I],
-                                  /*CreatePastEnd*/ true);
-        JT.Entries.push_back(Label);
+      bool HasOneParent = (JT.Parents.size() == 1);
+      for (unsigned I = 0; I < JT.EntriesAsAddress.size(); ++I) {
+        uint64_t EntryAddress = JT.EntriesAsAddress[I];
+        // builtin_unreachable does not belong to any function
+        // Need to handle separately
+        bool IsBuiltIn = false;
+        for (BinaryFunction *Parent : JT.Parents) {
+          if (EntryAddress == Parent->getAddress() + Parent->getSize()) {
+            IsBuiltIn = true;
+            // Specify second parameter as true to accept builtin_unreachable
+            MCSymbol *Label = getOrCreateLocalLabel(EntryAddress, true);
+            JT.Entries.push_back(Label);
+            break;
+          }
+        }
+        if (IsBuiltIn)
+          continue;
+        // Create local label for targets cannot be reached by other fragments
+        // Otherwise, secondary entry point to target function
+        BinaryFunction *TargetBF =
+            BC.getBinaryFunctionContainingAddress(EntryAddress);
+        if (TargetBF->getAddress() != EntryAddress) {
+          MCSymbol *Label =
+              (HasOneParent && TargetBF == this)
+                  ? getOrCreateLocalLabel(JT.EntriesAsAddress[I], true)
+                  : TargetBF->addEntryPointAtOffset(EntryAddress -
+                                                    TargetBF->getAddress());
+          JT.Entries.push_back(Label);
+        }
       }
     }
 
@@ -1672,7 +1696,8 @@ void BinaryFunction::postProcessJumpTables() {
 
     uint64_t EntryOffset = JTAddress - JT->getAddress();
     while (EntryOffset < JT->getSize()) {
-      uint64_t TargetOffset = JT->OffsetEntries[EntryOffset / JT->EntrySize];
+      uint64_t EntryAddress = JT->EntriesAsAddress[EntryOffset / JT->EntrySize];
+      uint64_t TargetOffset = EntryAddress - getAddress();
       if (TargetOffset < getSize()) {
         TakenBranches.emplace_back(JTSiteOffset, TargetOffset);
 
index 91c73f6..2b1035d 100644 (file)
@@ -29,9 +29,9 @@ extern cl::opt<unsigned> Verbosity;
 
 bolt::JumpTable::JumpTable(MCSymbol &Symbol, uint64_t Address, size_t EntrySize,
                            JumpTableType Type, LabelMapType &&Labels,
-                           BinaryFunction &BF, BinarySection &Section)
+                           BinarySection &Section)
     : BinaryData(Symbol, Address, 0, EntrySize, Section), EntrySize(EntrySize),
-      OutputEntrySize(EntrySize), Type(Type), Labels(Labels), Parent(&BF) {}
+      OutputEntrySize(EntrySize), Type(Type), Labels(Labels) {}
 
 std::pair<size_t, size_t>
 bolt::JumpTable::getEntriesForAddress(const uint64_t Addr) const {
@@ -103,11 +103,15 @@ void bolt::JumpTable::print(raw_ostream &OS) const {
   uint64_t Offset = 0;
   if (Type == JTT_PIC)
     OS << "PIC ";
-  OS << "Jump table " << getName() << " for function " << *Parent << " at 0x"
-     << Twine::utohexstr(getAddress()) << " with a total count of " << Count
-     << ":\n";
-  for (const uint64_t EntryOffset : OffsetEntries)
-    OS << "  0x" << Twine::utohexstr(EntryOffset) << '\n';
+  ListSeparator LS;
+
+  OS << "Jump table " << getName() << " for function ";
+  for (BinaryFunction *Frag : Parents)
+    OS << LS << *Frag;
+  OS << " at 0x" << Twine::utohexstr(getAddress()) << " with a total count of "
+     << Count << ":\n";
+  for (const uint64_t EntryAddress : EntriesAsAddress)
+    OS << "  absolute offset: 0x" << Twine::utohexstr(EntryAddress) << '\n';
   for (const MCSymbol *Entry : Entries) {
     auto LI = Labels.find(Offset);
     if (Offset && LI != Labels.end()) {
index fda787c..9d04216 100644 (file)
@@ -59,11 +59,10 @@ void dumpJumpTableFdata(raw_ostream &OS, const BinaryFunction &BF,
                         const MCInst &Instr, const std::string &BranchLabel) {
   StringRef FunctionName = BF.getOneName();
   const JumpTable *JT = BF.getJumpTable(Instr);
-  for (const uint64_t EntryOffset : JT->OffsetEntries) {
-    auto LI = JT->Labels.find(EntryOffset);
-    StringRef TargetName = LI->second->getName();
-    const uint64_t Mispreds = JT->Counts[EntryOffset].Mispreds;
-    const uint64_t Count = JT->Counts[EntryOffset].Count;
+  for (uint32_t i = 0; i < JT->Entries.size(); ++i) {
+    StringRef TargetName = JT->Entries[i]->getName();
+    const uint64_t Mispreds = JT->Counts[i].Mispreds;
+    const uint64_t Count = JT->Counts[i].Count;
     OS << "# FDATA: 1 " << FunctionName << " #" << BranchLabel << "# "
        << "1 " << FunctionName << " #" << TargetName << "# " << Mispreds << " "
        << Count << '\n';
index ddc4458..163b124 100644 (file)
@@ -2894,9 +2894,7 @@ void RewriteInstance::disassembleFunctions() {
   }
 
   BC->processInterproceduralReferences();
-  BC->clearJumpTableOffsets();
   BC->populateJumpTables();
-  BC->skipMarkedFragments();
 
   for (auto &BFI : BC->getBinaryFunctions()) {
     BinaryFunction &Function = BFI.second;
@@ -2908,6 +2906,7 @@ void RewriteInstance::disassembleFunctions() {
     Function.postProcessJumpTables();
   }
 
+  BC->clearJumpTableTempData();
   BC->adjustCodePadding();
 
   for (auto &BFI : BC->getBinaryFunctions()) {
@@ -2918,7 +2917,7 @@ void RewriteInstance::disassembleFunctions() {
 
     if (!Function.isSimple()) {
       assert((!BC->HasRelocations || Function.getSize() == 0 ||
-              Function.hasSplitJumpTable()) &&
+              Function.hasIndirectTargetToSplitFragment()) &&
              "unexpected non-simple function in relocation mode");
       continue;
     }
@@ -2974,6 +2973,11 @@ void RewriteInstance::buildFunctionsCFG() {
 }
 
 void RewriteInstance::postProcessFunctions() {
+  // We mark fragments as non-simple here, not during disassembly,
+  // So we can build their CFGs.
+  BC->skipMarkedFragments();
+  BC->clearFragmentsToSkip();
+
   BC->TotalScore = 0;
   BC->SumExecutionCount = 0;
   for (auto &BFI : BC->getBinaryFunctions()) {
diff --git a/bolt/test/X86/jump-table-move.s b/bolt/test/X86/jump-table-move.s
new file mode 100644 (file)
index 0000000..bfe0eea
--- /dev/null
@@ -0,0 +1,34 @@
+# REQUIRES: system-linux
+
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
+# RUN: %clang %cflags -no-pie %t.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt %t.exe -o %t.out --lite=0 -v=1 --jump-tables=move 2>&1 | FileCheck %s
+
+# CHECK-NOT: unclaimed PC-relative relocations left in data
+# CHECK: BOLT-INFO: marking main.cold.1 as a fragment of main
+# CHECK: BOLT-WARNING: Ignoring main.cold.1
+# CHECK: BOLT-WARNING: Ignoring main
+
+  .text
+  .globl main
+  .type main, %function
+  .p2align 2
+main:
+    cmpl  $0x67, %edi
+    jne main.cold.1
+LBB0:
+    retq
+.size main, .-main
+
+  .globl main.cold.1
+  .type main.cold.1, %function
+  .p2align 2
+main.cold.1:
+    jmpq  *JUMP_TABLE(,%rcx,8)
+.size main.cold.1, .-main.cold.1
+
+  .rodata
+  .globl JUMP_TABLE
+JUMP_TABLE:
+  .quad LBB0
+  .quad LBB0
index 7d0c0a3..caebe59 100644 (file)
@@ -8,9 +8,10 @@
 # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
 # RUN: llvm-strip --strip-unneeded %t.o
 # RUN: %clang %cflags %t.o -o %t.exe -Wl,-q
-# RUN: llvm-bolt -v=3 %t.exe -o %t.out 2>&1 | FileCheck %s
+# RUN: llvm-bolt -print-cfg -v=3 %t.exe -o %t.out 2>&1 | FileCheck %s
 
-# CHECK: BOLT-WARNING: Multiple fragments access same jump table: main; main.cold.1
+# CHECK: BOLT-INFO: Multiple fragments access same jump table: main; main.cold.1
+# CHECK: PIC Jump table JUMP_TABLE1 for function main, main.cold.1 at {{.*}}  with a total count of 0:
 
   .text
   .globl main