[PATCH] D127924: [BOLT] Resolve crash when optimizing Python

Huan Nguyen via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 15 17:23:04 PDT 2022


nhuhuan created this revision.
Herald added a reviewer: rafauler.
Herald added a subscriber: ayermolo.
Herald added a reviewer: Amir.
Herald added a reviewer: maksfb.
Herald added a project: All.
nhuhuan requested review of this revision.
Herald added subscribers: llvm-commits, yota9.
Herald added a project: LLVM.

Resolve a crash related to split functions

Due to split function optimization, a function can be splitted to two
fragments, and both fragments can access same jump table. This violates
the assumption that a jump table can only have one parent function,
leading to a crash when optimizing Python.

We want to support the case: different functions cannot access same
jump tables, but different fragments of same function can!

- As all fragments are from same function, we point JT::Parent to one

specific fragment. Right now it is the first disassembled fragment, but
we can point it to the function's main fragment later.

- Functions are disassembled sequentially. Previously, at the end of

processing a function, JT::OffsetEntries is cleared, so other fragment
can no longer reuse JT::OffsetEntries. To extend the support for split
function, we only clear JT::OffsetEntries after all functions are
disassembled.

- Let say A.hot and A.cold access JT of three targets {X, Y, Z}, where

X and Y are in A.hot, and Z is in A.cold. Suppose that A.hot is
disassembled first, JT::OffsetEntries = {X',Y',INVALID_OFFSET}. When
A.cold is disassembled, it cannot reuse JT::OffsetEntries above due to
different fragment start. A simple solution:
A.hot  = {X',Y',INVALID_OFFSET}
A.cold = {INVALID_OFFSET, INVALID_OFFSET, INVALID_OFFSET}

- We update the assertion to allow different fragments of same function

to get the same JumpTable object.

The next goal:
A.hot  = {X',Y',INVALID_OFFSET}
A.cold = {INVALID_OFFSET, INVALID_OFFSET, Z'}
The main issue is A.hot and A.cold have separate CFGs, thus jump table
targets are still constrained within fragment bounds.

The stretch goal:
A.hot  = {X, Y, Z}
A.cold = {X, Y, Z}


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D127924

Files:
  bolt/include/bolt/Core/BinaryContext.h
  bolt/lib/Core/BinaryContext.cpp
  bolt/lib/Core/BinaryFunction.cpp


Index: bolt/lib/Core/BinaryFunction.cpp
===================================================================
--- bolt/lib/Core/BinaryFunction.cpp
+++ bolt/lib/Core/BinaryFunction.cpp
@@ -1756,12 +1756,6 @@
   }
   clearList(JTSites);
 
-  // Free memory used by jump table offsets.
-  // for (auto &JTI : JumpTables) {
-  //   JumpTable &JT = *JTI.second;
-  //   clearList(JT.OffsetEntries);
-  // }
-
   // Conservatively populate all possible destinations for unknown indirect
   // branches.
   if (opts::StrictMode && hasInternalReference()) {
Index: bolt/lib/Core/BinaryContext.cpp
===================================================================
--- bolt/lib/Core/BinaryContext.cpp
+++ bolt/lib/Core/BinaryContext.cpp
@@ -761,19 +761,21 @@
 const MCSymbol *
 BinaryContext::getOrCreateJumpTable(BinaryFunction &Function, uint64_t Address,
                                     JumpTable::JumpTableType Type) {
+  auto isFragmentOf = [](BinaryFunction *Fragment, BinaryFunction *Parent) {
+    return (Fragment->isFragment() && Fragment->isParentFragment(Parent));
+  };
+
   if (JumpTable *JT = getJumpTableContainingAddress(Address)) {
     assert(JT->Type == Type && "jump table types have to match");
-    assert((JT->Parent == &Function ||
-            (JT->Parent->isFragment() &&
-             JT->Parent->isParentFragment(&Function)) ||
-            (Function.isFragment() && Function.isParentFragment(JT->Parent))) &&
+    assert((JT->Parent == &Function || isFragmentOf(JT->Parent, &Function) ||
+            isFragmentOf(&Function, JT->Parent)) &&
            "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 ((JT->Parent->isFragment() && JT->Parent->isParentFragment(&Function)) ||
-        (Function.isFragment() && Function.isParentFragment(JT->Parent))) {
+    if (isFragmentOf(JT->Parent, &Function) ||
+        isFragmentOf(&Function, JT->Parent)) {
       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;
Index: bolt/include/bolt/Core/BinaryContext.h
===================================================================
--- bolt/include/bolt/Core/BinaryContext.h
+++ bolt/include/bolt/Core/BinaryContext.h
@@ -481,7 +481,7 @@
   /// to function \p BF.
   std::string generateJumpTableName(const BinaryFunction &BF, uint64_t Address);
 
-  /// Free meory used by jump table offsets
+  /// Free memory used by jump table offsets
   void clearJumpTableOffsets() {
      for (auto& JTI: JumpTables) {
         JumpTable& JT = *JTI.second;


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D127924.437408.patch
Type: text/x-patch
Size: 2824 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20220616/8c780e2c/attachment.bin>


More information about the llvm-commits mailing list