[llvm] [SimplifyCFG] Use hash map to continue hoisting the common instructions (PR #78615)

via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 20 11:52:29 PDT 2024


================
@@ -1635,87 +1675,149 @@ bool SimplifyCFGOpt::hoistCommonCodeFromSuccessors(BasicBlock *BB,
   // many instructions we skip, serving as a compilation time control as well as
   // preventing excessive increase of life ranges.
   unsigned NumSkipped = 0;
-  // If we find an unreachable instruction at the beginning of a basic block, we
-  // can still hoist instructions from the rest of the basic blocks.
-  if (SuccIterPairs.size() > 2) {
-    erase_if(SuccIterPairs,
-             [](const auto &Pair) { return isa<UnreachableInst>(Pair.first); });
-    if (SuccIterPairs.size() < 2)
-      return false;
-  }
 
   bool Changed = false;
+  auto *SuccIterPairBegin = SuccIterPairs.begin();
+  ++SuccIterPairBegin;
+  auto BBItrPair = *SuccIterPairBegin++;
+  auto OtherSuccIterPairRange =
+      iterator_range(SuccIterPairBegin, SuccIterPairs.end());
+
+  DenseMap<llvm::hash_code, SmallVector<Instruction *, 2>> OtherSuccessorsHash;
+  DenseMap<Instruction *, unsigned> InstToSkipFlag;
 
+  unsigned skipFlag = 0;
+  Instruction *I = nullptr;
+  do {
+    I = &*BBItrPair.first;
+    auto HashValue = getHash(I);
+    skipFlag |= skippedInstrFlags(I);
+    // For the first successor we created hashmap for, put all instructions
+    // in the hashmap execept for the ones that have the same hash as some
+    // previous instruction in that BB.
+    if (OtherSuccessorsHash.find(HashValue) == OtherSuccessorsHash.end()) {
+      OtherSuccessorsHash[HashValue] = {I};
+      InstToSkipFlag[I] = skipFlag;
+    }
+    BBItrPair.first++;
+  } while (!I->isTerminator());
+
+  unsigned Index = 1;
+  for (auto BBItrPair : OtherSuccIterPairRange) {
+    // Fill the hashmap for every other successor
+    unsigned skipFlag = 0;
+    Instruction *I = nullptr;
+    do {
+      I = &*BBItrPair.first;
+      auto HashValue = getHash(I);
+      skipFlag |= skippedInstrFlags(I);
+      auto &InstVec = OtherSuccessorsHash[HashValue];
+      // For other successors put the instrcution in the map only if there are
+      // instructions with the same hash from other successors and this is the
+      // first instruction with this hash value from current successor.
+      if (OtherSuccessorsHash.find(HashValue) != OtherSuccessorsHash.end() &&
+          InstVec.size() == Index) {
+        InstVec.push_back(I);
+        InstToSkipFlag[I] = skipFlag;
+      }
+      BBItrPair.first++;
+    } while (!I->isTerminator());
+    Index++;
+  }
+
+  // Keep track of instructions skipped in the first successor
+  unsigned SkipFlagsBB1 = 0;
+  bool SameLevelHoist = true;
   for (;;) {
     auto *SuccIterPairBegin = SuccIterPairs.begin();
     auto &BB1ItrPair = *SuccIterPairBegin++;
     auto OtherSuccIterPairRange =
         iterator_range(SuccIterPairBegin, SuccIterPairs.end());
     auto OtherSuccIterRange = make_first_range(OtherSuccIterPairRange);
-
     Instruction *I1 = &*BB1ItrPair.first;
 
     // Skip debug info if it is not identical.
-    bool AllDbgInstsAreIdentical = all_of(OtherSuccIterRange, [I1](auto &Iter) {
+    bool IdenticalDebugs = all_of(OtherSuccIterRange, [I1](auto &Iter) {
       Instruction *I2 = &*Iter;
       return I1->isIdenticalToWhenDefined(I2);
     });
-    if (!AllDbgInstsAreIdentical) {
+    if (!IdenticalDebugs) {
       while (isa<DbgInfoIntrinsic>(I1))
         I1 = &*++BB1ItrPair.first;
-      for (auto &SuccIter : OtherSuccIterRange) {
-        Instruction *I2 = &*SuccIter;
-        while (isa<DbgInfoIntrinsic>(I2))
-          I2 = &*++SuccIter;
-      }
     }
-
-    bool AllInstsAreIdentical = true;
-    bool HasTerminator = I1->isTerminator();
-    for (auto &SuccIter : OtherSuccIterRange) {
-      Instruction *I2 = &*SuccIter;
-      HasTerminator |= I2->isTerminator();
-      if (AllInstsAreIdentical && !I1->isIdenticalToWhenDefined(I2))
-        AllInstsAreIdentical = false;
+    auto OtherInsts = OtherSuccessorsHash[getHash(I1)];
+
+    // Check if there are identical instructions in all other successors
+    // We might face with same hash values for different instructions.
+    // If that happens, ignore the instruction.
+    bool HasIdenticalInst =
+        OtherSuccessorsHash.find(getHash(I1)) != OtherSuccessorsHash.end() &&
+        OtherInsts.size() == (SuccIterPairs.size() - 1) &&
+        all_of(OtherInsts, [&](Instruction *I2) {
+          return I2->isIdenticalToWhenDefined(I1);
+        });
+
+    if (!HasIdenticalInst) {
+      if (NumSkipped >= HoistCommonSkipLimit)
+        return Changed;
+      SkipFlagsBB1 |= skippedInstrFlags(I1);
+      if (SameLevelHoist) {
+        for (auto &SuccIterPair : OtherSuccIterPairRange) {
+          Instruction *I = &*SuccIterPair.first++;
+          SuccIterPair.second |= skippedInstrFlags(I);
+        }
+      }
+      ++NumSkipped;
+      if (I1->isTerminator())
+        return Changed;
+      ++BB1ItrPair.first;
+      continue;
     }
 
-    SmallVector<Instruction *, 8> OtherInsts;
-    for (auto &SuccIter : OtherSuccIterRange)
-      OtherInsts.push_back(&*SuccIter);
-
     // If we are hoisting the terminator instruction, don't move one (making a
     // broken BB), instead clone it, and remove BI.
-    if (HasTerminator) {
+    if (I1->isTerminator()) {
       // Even if BB, which contains only one unreachable instruction, is ignored
       // at the beginning of the loop, we can hoist the terminator instruction.
       // If any instructions remain in the block, we cannot hoist terminators.
-      if (NumSkipped || !AllInstsAreIdentical) {
+      if (NumSkipped) {
         hoistLockstepIdenticalDPValues(TI, I1, OtherInsts);
         return Changed;
       }
-
-      return hoistSuccIdenticalTerminatorToSwitchOrIf(TI, I1, OtherInsts) ||
-             Changed;
+      SmallVector<Instruction *, 8> Insts;
+      for (auto *I2 : OtherInsts) {
+        // BB holding I2 should only contain the branch instruction
+        auto itr = I2->getParent()->instructionsWithoutDebug();
+        if (&*itr.begin() != I2)
+          return Changed;
+        Insts.push_back(I2);
+      }
+      return hoistSuccIdenticalTerminatorToSwitchOrIf(TI, I1, Insts) || Changed;
     }
 
-    if (AllInstsAreIdentical) {
-      unsigned SkipFlagsBB1 = BB1ItrPair.second;
-      AllInstsAreIdentical =
-          isSafeToHoistInstr(I1, SkipFlagsBB1) &&
-          all_of(OtherSuccIterPairRange, [=](const auto &Pair) {
-            Instruction *I2 = &*Pair.first;
-            unsigned SkipFlagsBB2 = Pair.second;
-            // Even if the instructions are identical, it may not
-            // be safe to hoist them if we have skipped over
-            // instructions with side effects or their operands
-            // weren't hoisted.
-            return isSafeToHoistInstr(I2, SkipFlagsBB2) &&
-                   shouldHoistCommonInstructions(I1, I2, TTI);
-          });
+    bool SafeToHoist = isSafeToHoistInstr(I1, SkipFlagsBB1);
+    for (auto [SuccIterPair, I2] : zip(OtherSuccIterPairRange, OtherInsts)) {
+      // If instructions of all successors are at the same level, use the
----------------
goldsteinn wrote:

Think simpler and faster would just be put `if(!SafeToHoist) break`. then get rid of the `SafeToHoist &&` below.

https://github.com/llvm/llvm-project/pull/78615


More information about the llvm-commits mailing list