https://github.com/bgergely0 updated 
https://github.com/llvm/llvm-project/pull/163381

From 6ad31fe5310dc68620f75b72cc10125a5792075b Mon Sep 17 00:00:00 2001
From: Gergely Balint <[email protected]>
Date: Tue, 7 Oct 2025 14:01:47 +0000
Subject: [PATCH] [BOLT] Improve InsertNegateRAStatePass::inferUnknownStates

Previous implementation used a simple heuristic. This can be improved in
several ways:
- If a BasicBlock has instruction both with known RAState and unknown RAState,
  use the known states to work out the unknown ones.
- If a BasicBlock only consists of instructions with unknown RAState,
  use the last known RAState from its predecessors, or the first known
  from its successors to set the RAStates in the BasicBlock. This includes
  error checking: all predecessors/successors should have the same RAState.
- Some BasicBlocks may only contain instructions with unknown RAState,
  and have no CFG neighbors. These already have incorrect unwind info.
  For these, we copy the last known RAState based on the layout order.

Updated bolt/docs/PacRetDesign.md to reflect changes.
---
 bolt/docs/PacRetDesign.md                     |  23 +-
 .../bolt/Passes/InsertNegateRAStatePass.h     |  34 ++-
 bolt/lib/Passes/InsertNegateRAStatePass.cpp   | 228 ++++++++++++++++--
 3 files changed, 257 insertions(+), 28 deletions(-)

diff --git a/bolt/docs/PacRetDesign.md b/bolt/docs/PacRetDesign.md
index f3fe5fbd522cb..c7c76cac3a100 100644
--- a/bolt/docs/PacRetDesign.md
+++ b/bolt/docs/PacRetDesign.md
@@ -200,16 +200,29 @@ This pass runs after optimizations. It performns the 
_inverse_ of MarkRAState pa
 Some BOLT passes can add new Instructions. In InsertNegateRAStatePass, we have
 to know what RA state these have.
 
-The current solution has the `inferUnknownStates` function to cover these, 
using
-a fairly simple strategy: unknown states inherit the last known state.
-
-This will be updated to a more robust solution.
-
 > [!important]
 > As issue #160989 describes, unwind info is incorrect in stubs with multiple 
 > callers.
 > For this same reason, we cannot generate correct pac-specific unwind info: 
 > the signess
 > of the _incorrect_ return address is meaningless.
 
+Assignment of RAStates to newly generated instructions is done in 
`inferUnknownStates`.
+We have three different cases to cover:
+
+1. If a BasicBlock has some instructions with known RA state, and some 
without, we
+   can copy the RAState of known instructions to the unknown ones. As the 
control
+   flow only changes between BasicBlocks, instructions in the same BasicBlock 
have the
+   same return address.
+
+2. If all instructions in a BasicBlock are unknown, we can look at all CFG 
neighbors
+   (that is predecessors/successors). The RAState should be the same as of the
+   neighboring blocks. Conflicting RAStates in neighbors indicate an error. 
Such
+   functions should be ignored.
+
+3. If a BasicBlock has no CFG neighbors, we have to copy the RAState of the 
previous
+    BasicBlock in layout order.
+
+If any BasicBlocks remain with unknown instructions, the function will be 
ignored.
+
 ### Optimizations requiring special attention
 
 Marking states before optimizations ensure that instructions can be moved 
around
diff --git a/bolt/include/bolt/Passes/InsertNegateRAStatePass.h 
b/bolt/include/bolt/Passes/InsertNegateRAStatePass.h
index 836948bf5e9c0..b4b428207b657 100644
--- a/bolt/include/bolt/Passes/InsertNegateRAStatePass.h
+++ b/bolt/include/bolt/Passes/InsertNegateRAStatePass.h
@@ -1,4 +1,4 @@
-//===- bolt/Passes/InsertNegateRAStatePass.cpp 
----------------------------===//
+//===- bolt/Passes/InsertNegateRAStatePass.h 
------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -30,9 +30,39 @@ class InsertNegateRAState : public BinaryFunctionPass {
 private:
   /// Because states are tracked as MCAnnotations on individual instructions,
   /// newly inserted instructions do not have a state associated with them.
-  /// New states are "inherited" from the last known state.
   void inferUnknownStates(BinaryFunction &BF);
 
+  /// Simple case: copy RAStates to unknown insts from previous inst.
+  /// Account for signing and authenticating insts.
+  void fillUnknownStateInBB(BinaryContext &BC, BinaryBasicBlock &BB);
+
+  /// Fill unknown RAStates in BBs with no successors/predecessors. These are
+  /// Stubs inserted by LongJmp. As of #160989, we have to copy the RAState 
from
+  /// the previous BB in the layout, because CFIs are already incorrect here.
+  void fillUnknownStubs(BinaryFunction &BF);
+
+  /// Fills unknowns RAStates of BBs with successors/predecessors. Uses
+  /// getRAStateByCFG to determine the RAState. Does more than one iteration if
+  /// needed. Reports an error, if it cannot find the RAState for all BBs with
+  /// predecessors/successors.
+  void fillUnknownBlocksInCFG(BinaryFunction &BF);
+
+  /// For BBs which only hold instructions with unknown RAState, we check
+  /// CFG neighbors (successors, predecessors) of the BB. If they have 
different
+  /// RAStates, we report an inconsistency. Otherwise, we return the found
+  /// RAState.
+  std::optional<bool> getRAStateByCFG(BinaryBasicBlock &BB, BinaryFunction 
&BF);
+  /// Returns the first known RAState from \p BB, or std::nullopt if all are
+  /// unknown.
+  std::optional<bool> getFirstKnownRAState(BinaryContext &BC,
+                                           BinaryBasicBlock &BB);
+
+  /// \p Return true if all instructions have unknown RAState.
+  bool isUnknownBlock(BinaryContext &BC, BinaryBasicBlock &BB);
+
+  /// Set all instructions in \p BB to \p State.
+  void markUnknownBlock(BinaryContext &BC, BinaryBasicBlock &BB, bool State);
+
   /// Support for function splitting:
   /// if two consecutive BBs with Signed state are going to end up in different
   /// functions (so are held by different FunctionFragments), we have to add a
diff --git a/bolt/lib/Passes/InsertNegateRAStatePass.cpp 
b/bolt/lib/Passes/InsertNegateRAStatePass.cpp
index 9aa7c33bd1d6e..dd3565ba4a077 100644
--- a/bolt/lib/Passes/InsertNegateRAStatePass.cpp
+++ b/bolt/lib/Passes/InsertNegateRAStatePass.cpp
@@ -74,6 +74,20 @@ void InsertNegateRAState::runOnFunction(BinaryFunction &BF) {
   }
 }
 
+void InsertNegateRAState::inferUnknownStates(BinaryFunction &BF) {
+  BinaryContext &BC = BF.getBinaryContext();
+
+  // Fill in missing RAStates in simple cases (inside BBs).
+  for (BinaryBasicBlock &BB : BF) {
+    fillUnknownStateInBB(BC, BB);
+  }
+  // Some stubs have no predecessors. For those, we iterate once in the layout
+  // order to fill their RAState.
+  fillUnknownStubs(BF);
+
+  fillUnknownBlocksInCFG(BF);
+}
+
 void InsertNegateRAState::coverFunctionFragmentStart(BinaryFunction &BF,
                                                      FunctionFragment &FF) {
   BinaryContext &BC = BF.getBinaryContext();
@@ -91,7 +105,8 @@ void 
InsertNegateRAState::coverFunctionFragmentStart(BinaryFunction &BF,
       });
   // If a function is already split in the input, the first FF can also start
   // with Signed state. This covers that scenario as well.
-  auto RAState = BC.MIB->getRAState(*(*FirstNonEmpty)->begin());
+  auto II = (*FirstNonEmpty)->getFirstNonPseudo();
+  auto RAState = BC.MIB->getRAState(*II);
   if (!RAState) {
     BC.errs() << "BOLT-ERROR: unknown RAState after inferUnknownStates "
               << " in function " << BF.getPrintName() << "\n";
@@ -99,36 +114,207 @@ void 
InsertNegateRAState::coverFunctionFragmentStart(BinaryFunction &BF,
     return;
   }
   if (*RAState)
-    BF.addCFIInstruction(*FirstNonEmpty, (*FirstNonEmpty)->begin(),
+    BF.addCFIInstruction(*FirstNonEmpty, II,
                          MCCFIInstruction::createNegateRAState(nullptr));
 }
 
-void InsertNegateRAState::inferUnknownStates(BinaryFunction &BF) {
+std::optional<bool>
+InsertNegateRAState::getFirstKnownRAState(BinaryContext &BC,
+                                          BinaryBasicBlock &BB) {
+  for (const MCInst &Inst : BB) {
+    if (BC.MIB->isCFI(Inst))
+      continue;
+    auto RAStateOpt = BC.MIB->getRAState(Inst);
+    if (RAStateOpt)
+      return RAStateOpt;
+  }
+  return std::nullopt;
+}
+
+void InsertNegateRAState::fillUnknownStateInBB(BinaryContext &BC,
+                                               BinaryBasicBlock &BB) {
+
+  auto First = BB.getFirstNonPseudo();
+  if (First == BB.end())
+    return;
+  // If the first instruction has unknown RAState, we should copy the first
+  // known RAState.
+  auto RAStateOpt = BC.MIB->getRAState(*First);
+  if (!RAStateOpt) {
+    auto FirstRAState = getFirstKnownRAState(BC, BB);
+    if (!FirstRAState)
+      // We fill unknown BBs later.
+      return;
+
+    BC.MIB->setRAState(*First, *FirstRAState);
+  }
+
+  // At this point we know the RAState of the first instruction,
+  // so we can propagate the RAStates to all subsequent unknown instructions.
+  MCInst Prev = *First;
+  for (auto It = BB.begin() + 1; It != BB.end(); ++It) {
+    MCInst &Inst = *It;
+    if (BC.MIB->isCFI(Inst))
+      continue;
+
+    auto PrevRAState = BC.MIB->getRAState(Prev);
+    if (!PrevRAState)
+      llvm_unreachable("Previous Instruction has no RAState.");
+
+    auto RAState = BC.MIB->getRAState(Inst);
+    if (!RAState) {
+      if (BC.MIB->isPSignOnLR(Prev))
+        PrevRAState = true;
+      else if (BC.MIB->isPAuthOnLR(Prev))
+        PrevRAState = false;
+      BC.MIB->setRAState(Inst, *PrevRAState);
+    }
+    Prev = Inst;
+  }
+}
+
+bool InsertNegateRAState::isUnknownBlock(BinaryContext &BC,
+                                         BinaryBasicBlock &BB) {
+  for (const MCInst &Inst : BB) {
+    if (BC.MIB->isCFI(Inst))
+      continue;
+    auto RAState = BC.MIB->getRAState(Inst);
+    if (RAState)
+      return false;
+  }
+  return true;
+}
+
+void InsertNegateRAState::markUnknownBlock(BinaryContext &BC,
+                                           BinaryBasicBlock &BB, bool State) {
+  // If we call this when an Instruction has either kRASigned or kRAUnsigned
+  // annotation, setRASigned or setRAUnsigned would fail.
+  assert(isUnknownBlock(BC, BB) &&
+         "markUnknownBlock should only be called on unknown blocks");
+  for (MCInst &Inst : BB) {
+    if (BC.MIB->isCFI(Inst))
+      continue;
+    BC.MIB->setRAState(Inst, State);
+  }
+}
+
+std::optional<bool> InsertNegateRAState::getRAStateByCFG(BinaryBasicBlock &BB,
+                                                         BinaryFunction &BF) {
   BinaryContext &BC = BF.getBinaryContext();
-  bool FirstIter = true;
-  MCInst PrevInst;
-  for (BinaryBasicBlock &BB : BF) {
-    for (MCInst &Inst : BB) {
-      if (BC.MIB->isCFI(Inst))
+
+  auto checkRAState = [&](std::optional<bool> &NeighborRAState, MCInst &Inst) {
+    auto RAState = BC.MIB->getRAState(Inst);
+    if (!RAState)
+      return;
+    if (!NeighborRAState) {
+      NeighborRAState = *RAState;
+      return;
+    }
+    if (NeighborRAState != *RAState) {
+      BC.outs() << "BOLT-WARNING: Conflicting RAState found in function "
+                << BF.getPrintName() << ". Function will not be optimized.\n";
+      BF.setIgnored();
+    }
+  };
+
+  // Holds the first found RAState from CFG neighbors.
+  std::optional<bool> NeighborRAState = std::nullopt;
+  if (BB.pred_size() != 0) {
+    for (BinaryBasicBlock *PredBB : BB.predecessors()) {
+      //  find last inst of Predecessor with known RA State.
+      auto LI = PredBB->getLastNonPseudo();
+      if (LI == PredBB->rend())
+        continue;
+      MCInst &LastInst = *LI;
+      checkRAState(NeighborRAState, LastInst);
+    }
+  } else if (BB.succ_size() != 0) {
+    for (BinaryBasicBlock *SuccBB : BB.successors()) {
+      //  find first inst of Successor with known RA State.
+      auto FI = SuccBB->getFirstNonPseudo();
+      if (FI == SuccBB->end())
         continue;
+      MCInst &FirstInst = *FI;
+      checkRAState(NeighborRAState, FirstInst);
+    }
+  } else {
+    llvm_unreachable("Called getRAStateByCFG on a BB with no preds or succs.");
+  }
+
+  return NeighborRAState;
+}
 
-      auto RAState = BC.MIB->getRAState(Inst);
-      if (!FirstIter && !RAState) {
-        if (BC.MIB->isPSignOnLR(PrevInst))
-          RAState = true;
-        else if (BC.MIB->isPAuthOnLR(PrevInst))
-          RAState = false;
-        else {
+void InsertNegateRAState::fillUnknownStubs(BinaryFunction &BF) {
+  BinaryContext &BC = BF.getBinaryContext();
+  bool FirstIter = true;
+  MCInst PrevInst;
+  for (FunctionFragment &FF : BF.getLayout().fragments()) {
+    for (BinaryBasicBlock *BB : FF) {
+      if (!FirstIter && isUnknownBlock(BC, *BB)) {
+        // If we have no predecessors or successors, current BB is a Stub 
called
+        // from another BinaryFunction. As of #160989, we have to copy the
+        // PrevInst's RAState, because CFIs are already incorrect here.
+        if (BB->pred_size() == 0 && BB->succ_size() == 0) {
           auto PrevRAState = BC.MIB->getRAState(PrevInst);
-          RAState = PrevRAState ? *PrevRAState : false;
+          if (!PrevRAState) {
+            BF.dump();
+            BB->dump();
+            llvm_unreachable(
+                "Previous Instruction has no RAState in fillUnknownStubs.");
+            continue;
+          }
+
+          if (BC.MIB->isPSignOnLR(PrevInst)) {
+            PrevRAState = true;
+          } else if (BC.MIB->isPAuthOnLR(PrevInst)) {
+            PrevRAState = false;
+          }
+          markUnknownBlock(BC, *BB, *PrevRAState);
         }
-        BC.MIB->setRAState(Inst, *RAState);
-      } else {
+      }
+      if (FirstIter) {
         FirstIter = false;
-        if (!RAState)
-          BC.MIB->setRAState(Inst, BF.getInitialRAState());
+        if (isUnknownBlock(BC, *BB))
+          markUnknownBlock(BC, *BB, false);
       }
-      PrevInst = Inst;
+      auto Last = BB->getLastNonPseudo();
+      if (Last != BB->rend())
+        PrevInst = *Last;
+    }
+  }
+}
+void InsertNegateRAState::fillUnknownBlocksInCFG(BinaryFunction &BF) {
+  BinaryContext &BC = BF.getBinaryContext();
+
+  auto fillUnknowns = [&](BinaryFunction &BF) -> std::pair<int, bool> {
+    int Unknowns = 0;
+    bool Updated = false;
+    for (BinaryBasicBlock &BB : BF) {
+      // Only try to iterate if the BB has either predecessors or successors.
+      if (isUnknownBlock(BC, BB) &&
+          (BB.pred_size() != 0 || BB.succ_size() != 0)) {
+        auto RAStateOpt = getRAStateByCFG(BB, BF);
+        if (RAStateOpt) {
+          markUnknownBlock(BC, BB, *RAStateOpt);
+          Updated = true;
+        } else {
+          Unknowns++;
+        }
+      }
+    }
+    return std::pair<int, bool>{Unknowns, Updated};
+  };
+
+  while (true) {
+    std::pair<int, bool> Iter = fillUnknowns(BF);
+    if (Iter.first == 0)
+      break;
+    if (!Iter.second) {
+      BC.errs() << "BOLT-WARNING: Could not infer RAState for " << Iter.first
+                << " BBs in function " << BF.getPrintName()
+                << ". Function will not be optimized.\n";
+      BF.setIgnored();
+      break;
     }
   }
 }

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to