[llvm] [FMV][GlobalOpt] Bypass the IFunc Resolver of MultiVersioned functions. (PR #87939)

Alexandros Lamprineas via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 8 07:18:19 PDT 2024


================
@@ -2462,6 +2462,228 @@ DeleteDeadIFuncs(Module &M,
   return Changed;
 }
 
+static Function *foldResolverForCallSite(CallBase *CS, uint64_t Priority,
+                                         TargetTransformInfo &TTI) {
+  // Look for the instruction which feeds the feature mask to the users.
+  auto findRoot = [&TTI](Function *F) -> Instruction * {
+    for (Instruction &I : F->getEntryBlock())
+      if (auto *Load = dyn_cast<LoadInst>(&I))
+        if (Load->getPointerOperand() == TTI.getCPUFeatures(*F->getParent()))
+          return Load;
+    return nullptr;
+  };
+
+  auto *IF = cast<GlobalIFunc>(CS->getCalledOperand());
+  Instruction *Root = findRoot(IF->getResolverFunction());
+  // There is no such instruction. Bail.
+  if (!Root)
+    return nullptr;
+
+  // Create a constant mask to use as seed for the constant propagation.
+  Constant *Seed = Constant::getIntegerValue(
+      Root->getType(), APInt(Root->getType()->getIntegerBitWidth(), Priority));
+
+  auto DL = CS->getModule()->getDataLayout();
+
+  // Recursively propagate on single use chains.
+  std::function<Constant *(Instruction *, Instruction *, Constant *,
+                           BasicBlock *)>
+      constFoldInst = [&](Instruction *I, Instruction *Use, Constant *C,
+                          BasicBlock *Pred) -> Constant * {
+    // Base case.
+    if (auto *Ret = dyn_cast<ReturnInst>(I))
+      if (Ret->getReturnValue() == Use)
+        return C;
+
+    // Minimal set of instruction types to handle.
+    if (auto *BinOp = dyn_cast<BinaryOperator>(I)) {
+      bool Swap = BinOp->getOperand(1) == Use;
+      if (auto *Other = dyn_cast<Constant>(BinOp->getOperand(Swap ? 0 : 1)))
+        C = Swap ? ConstantFoldBinaryInstruction(BinOp->getOpcode(), Other, C)
+                 : ConstantFoldBinaryInstruction(BinOp->getOpcode(), C, Other);
+    } else if (auto *Cmp = dyn_cast<CmpInst>(I)) {
+      bool Swap = Cmp->getOperand(1) == Use;
+      if (auto *Other = dyn_cast<Constant>(Cmp->getOperand(Swap ? 0 : 1)))
+        C = Swap ? ConstantFoldCompareInstOperands(Cmp->getPredicate(), Other,
+                                                   C, DL)
+                 : ConstantFoldCompareInstOperands(Cmp->getPredicate(), C,
+                                                   Other, DL);
+    } else if (auto *Sel = dyn_cast<SelectInst>(I)) {
+      if (Sel->getCondition() == Use)
+        C = dyn_cast<Constant>(C->isZeroValue() ? Sel->getFalseValue()
+                                                : Sel->getTrueValue());
+    } else if (auto *Phi = dyn_cast<PHINode>(I)) {
+      if (Pred)
+        C = dyn_cast<Constant>(Phi->getIncomingValueForBlock(Pred));
+    } else if (auto *Br = dyn_cast<BranchInst>(I)) {
+      if (Br->getCondition() == Use) {
+        BasicBlock *BB = Br->getSuccessor(C->isZeroValue());
+        return constFoldInst(&BB->front(), Root, Seed, Br->getParent());
+      }
+    } else {
+      // Don't know how to handle. Bail.
----------------
labrinea wrote:

There can be various reasons of early exit, not only unhandled instruction in the resolver. For example see line 2613 and 2676. Do we need to differentiate them? Line 2613 suggests the resolver didn't look as expected, line 2676 suggests we failed to constant fold, which basically implies the resolver didn't look as expected (the handled instruction operands weren't constants), so as this line here. I don't see value in collecting stats for each of these cases treating it a separate instance.

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


More information about the llvm-commits mailing list