[llvm] [CodeGen] [EarlyIfConversion] Prioritize conversion of hard to predict branches (PR #174457)

Jon Roelofs via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 6 11:22:55 PST 2026


================
@@ -896,6 +908,142 @@ void EarlyIfConverter::invalidateTraces() {
   Traces->verifyAnalysis();
 }
 
+/// Check if a register's value comes from a memory load by walking the
+/// def-use chain. Results are cached for virtual registers only.
+bool EarlyIfConverter::doOperandsComeFromMemory(Register Reg) {
+  if (!Reg.isVirtual())
+    return false;
+
+  // Check cache first.
+  auto It = OperandMemoryCache.find(Reg);
+  if (It != OperandMemoryCache.end())
+    return It->second;
+
+  // Walk the def-use chain.
+  SmallPtrSet<const MachineInstr *, 16> Visited;
+  SmallVector<const MachineInstr *, 16> Worklist;
+  SmallVector<Register, 16> VisitedRegs;
+
+  MachineInstr *DefMI = MRI->getVRegDef(Reg);
+  // The operand is defined outside of the function - it does not
+  // come from memory access.
+  if (!DefMI) {
+    OperandMemoryCache[Reg] = false;
+    return false;
+  }
+
+  Worklist.push_back(DefMI);
+  VisitedRegs.push_back(Reg);
+
+  while (!Worklist.empty()) {
+    const MachineInstr *MI = Worklist.pop_back_val();
+    if (!Visited.insert(MI).second)
+      continue;
+
+    // Check if this instruction is a load.
+    if (MI->mayLoad()) {
+      LLVM_DEBUG(dbgs() << "Operand comes from load: " << *MI);
+      // Cache all visited virtual registers as coming from memory.
+      for (Register Reg : VisitedRegs)
+        OperandMemoryCache[Reg] = true;
+      return true;
+    }
+
+    // Walk through all register use operands and find their definitions.
+    for (const MachineOperand &MO : MI->operands()) {
+      if (!MO.isReg() || !MO.isUse())
+        continue;
+      Register UseReg = MO.getReg();
+      if (!UseReg.isVirtual())
+        continue;
+
+      // Check cache for this operand.
+      auto CacheIt = OperandMemoryCache.find(UseReg);
+      if (CacheIt != OperandMemoryCache.end()) {
+        if (CacheIt->second) {
+          for (Register Reg : VisitedRegs)
+            OperandMemoryCache[Reg] = true;
+          return true;
+        }
+        continue;
+      }
+
+      MachineInstr *UseDef = MRI->getVRegDef(UseReg);
+      if (UseDef && !Visited.count(UseDef)) {
+        Worklist.push_back(UseDef);
+        VisitedRegs.push_back(UseReg);
+      }
+    }
+  }
+
+  // No load found, cache all visited virtual registers as not from memory.
+  for (Register Reg : VisitedRegs)
+    OperandMemoryCache[Reg] = false;
+  return false;
+}
+
+/// Check if the branch condition is data-dependent (comes from memory loads).
+bool EarlyIfConverter::isConditionDataDependent() {
+  MachineBasicBlock::iterator BranchIt = IfConv.Head->getFirstTerminator();
+  if (BranchIt == IfConv.Head->end())
+    return false;
+
+  // Get the status flags register from the target. If the target hasn't
+  // implemented this hook, we skip physical register analysis entirely
+  // and fallback to the previous behavior.
+  MCRegister StatusFlagsReg = TRI->getStatusFlagsRegister();
+
+  if (!StatusFlagsReg.isValid())
+    return false;
+
+  for (const MachineOperand &MO : BranchIt->operands()) {
+    if (!MO.isReg() || !MO.isUse())
+      continue;
+    Register Reg = MO.getReg();
+
+    if (Reg.isVirtual()) {
+      if (doOperandsComeFromMemory(Reg)) {
+        LLVM_DEBUG(dbgs() << "Condition is data-dependent (from memory)\n");
+        ++NumDataDependant;
+        return true;
+      }
+    } else if (Reg.isPhysical() && TRI->regsOverlap(Reg, StatusFlagsReg)) {
+      // For the status flags register (e.g., NZCV on AArch64),
+      // search backwards to find the defining instruction.
+      for (auto I = std::prev(BranchIt);; --I) {
+        // Check if this instruction defines the status flags register.
+        bool FoundDef = false;
+        for (const MachineOperand &DefMO : I->operands()) {
+          if (DefMO.isReg() && DefMO.isDef() &&
+              TRI->regsOverlap(DefMO.getReg(), StatusFlagsReg)) {
+            FoundDef = true;
+            break;
+          }
+        }
+
+        if (FoundDef) {
+          for (const MachineOperand &DefMO : I->operands()) {
+            if (!DefMO.isReg() || !DefMO.isUse())
+              continue;
+            Register DefReg = DefMO.getReg();
+            if (DefReg.isVirtual() && doOperandsComeFromMemory(DefReg)) {
+              LLVM_DEBUG(dbgs()
+                         << "Condition is data-dependent (from memory)\n");
+              ++NumDataDependant;
+              return true;
+            }
+          }
+          break;
+        }
+
+        if (I == IfConv.Head->begin())
----------------
jroelofs wrote:

should this go in the middle `; ;` of the `for`? feels a bit weird for that to be empty, and this to be here.

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


More information about the llvm-commits mailing list