[llvm] [InstCombine] Canonicalize complex boolean expressions into ~((y | z) ^ x) via 3-input truth table (PR #149530)

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 20 11:07:30 PDT 2025


================
@@ -50,6 +51,213 @@ static Value *getFCmpValue(unsigned Code, Value *LHS, Value *RHS,
   return Builder.CreateFCmpFMF(NewPred, LHS, RHS, FMF);
 }
 
+/// This is to create optimal 3-variable boolean logic from truth tables.
+/// currently it supports the cases pertaining to the issue 97044. More cases
+/// can be added based on real-world justification for specific 3 input cases
+///  or with reviewer approval all 256 cases can be added (choose the
+///  canonicalizations found
+/// in x86InstCombine.cpp?)
+static Value *createLogicFromTable3Var(const std::bitset<8> &Table, Value *Op0,
+                                       Value *Op1, Value *Op2, Value *Root,
+                                       IRBuilderBase &Builder) {
+  uint8_t TruthValue = Table.to_ulong();
+  auto FoldConstant = [&](bool Val) {
+    Type *Ty = Op0->getType();
+    return Val ? ConstantInt::getTrue(Ty) : ConstantInt::getFalse(Ty);
+  };
+
+  Value *Result = nullptr;
+  switch (TruthValue) {
+  default:
+    return nullptr;
+  case 0x00: // Always FALSE
+    Result = FoldConstant(false);
+    break;
+  case 0xFF: // Always TRUE
+    Result = FoldConstant(true);
+    break;
+  case 0xE1: // ~((Op1 | Op2) ^ Op0)
+  {
+    Value *Or = Builder.CreateOr(Op1, Op2);
+    Value *Xor = Builder.CreateXor(Or, Op0);
+    Result = Builder.CreateNot(Xor);
+  } break;
+  case 0x60: // Op0 & (Op1 ^ Op2)
+  {
+    Value *Xor = Builder.CreateXor(Op1, Op2);
+    Result = Builder.CreateAnd(Op0, Xor);
+  } break;
+  case 0xD2: // ((Op1 | Op2) ^ Op0) ^ Op1
+  {
+    Value *Or = Builder.CreateOr(Op1, Op2);
+    Value *Xor1 = Builder.CreateXor(Or, Op0);
+    Result = Builder.CreateXor(Xor1, Op1);
+  } break;
+  }
+
+  return Result;
+}
+
+static std::tuple<Value *, Value *, Value *, SmallVector<Instruction *>>
+extractThreeVariablesAndInstructions(Value *Root) {
+  SmallPtrSet<Value *, 3> Variables;
+  SmallPtrSet<Value *, 32> Visited; // Prevent hanging during loop unrolling
+                                    // (see bitreverse-hang.ll)
+  SmallVector<Instruction *> Instructions;
+  SmallVector<Value *> Worklist;
+  Worklist.push_back(Root);
+
+  while (!Worklist.empty()) {
+    Value *V = Worklist.pop_back_val();
+
+    if (!Visited.insert(V).second)
+      continue;
+
+    Value *NotV;
+    if (match(V, m_Not(m_Value(NotV)))) {
+      if (auto *I = dyn_cast<Instruction>(V))
+        Instructions.push_back(I);
+      if (V == Root ||
+          V->hasOneUse()) { // Due to lack of cost-based heuristic, only
+                            // traverse if it belongs to this expression tree
+        Worklist.push_back(NotV);
+      }
+      continue;
+    }
+    if (auto *BO = dyn_cast<BinaryOperator>(V)) {
+      if (!BO->isBitwiseLogicOp())
+        return {nullptr, nullptr, nullptr, {}};
+
+      Instructions.push_back(BO);
+
+      if (V == Root || V->hasOneUse()) {
+        Worklist.push_back(BO->getOperand(0));
+        Worklist.push_back(BO->getOperand(1));
+      }
+    } else if (isa<Argument>(V) || isa<Instruction>(V)) {
+      if (V != Root) {
+        Variables.insert(V);
+      }
+    }
+  }
+
+  if (Variables.size() == 3) {
+    // Check that all instruction variables are in the same BB
+    SmallVector<Value *, 3> SortedVars(Variables.begin(), Variables.end());
+    BasicBlock *FirstBB = nullptr;
+    for (Value *V : SortedVars) {
+      if (auto *I = dyn_cast<Instruction>(V)) {
+        if (!FirstBB) {
+          FirstBB = I->getParent();
+        } else if (I->getParent() != FirstBB) {
+          return {nullptr, nullptr, nullptr, {}};
+        }
+      }
+    }
+
+    // Validation that all collected instructions have operands that will be in
+    // Computed map
+    SmallPtrSet<Value *, 32> ValidOperands(Variables.begin(), Variables.end());
+    ValidOperands.insert(Instructions.begin(), Instructions.end());
+
+    for (Instruction *I : Instructions) {
+      Value *NotV;
+      if (match(I, m_Not(m_Value(NotV)))) {
+        // For NOT operations, only check the variable operand (constant -1 is
+        // handled by pattern matcher)
+        if (!ValidOperands.count(NotV))
+          return {nullptr, nullptr, nullptr, {}};
+      } else {
+        for (Use &U : I->operands()) {
+          if (!ValidOperands.count(U.get()))
+            return {nullptr, nullptr, nullptr, {}};
+        }
+      }
+    }
+
+    // Sort variables by instruction order
+    llvm::sort(SortedVars, [](Value *A, Value *B) {
+      if (auto *IA = dyn_cast<Instruction>(A))
+        if (auto *IB = dyn_cast<Instruction>(B))
+          return IA->comesBefore(IB);
+      return A < B;
----------------
yafet-a wrote:

Thanks for the suggestion, this has been done now in the latest commits

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


More information about the llvm-commits mailing list