[llvm] [CVP] Replace constant call args with known-equal function params (PR #196715)

via llvm-commits llvm-commits at lists.llvm.org
Sat May 9 05:13:35 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Aayush Shrivastava (iamaayushrivastava)

<details>
<summary>Changes</summary>

Main Reason: `CorrelatedValuePropagation::processCallSite` already replaces *variable* arguments with their known-constant values. It did not do the reverse: replace a *constant* argument `C` with a function parameter known to equal `C` at the call site.

Before IR:  call @<!-- -->fdecl_0(i32 noundef 0, i64 noundef 0)                                                 
After  IR:  call @<!-- -->fdecl_0(i32 noundef %a, i64 noundef 0)                                                

(`i64 0` is unchanged - type mismatch with the `i32` parameter.)
                                                                                               
## x86-64 codegen improvement                                                                           
                                                                          
  ```asm                                                                                                  
  ; Before (extra xor edi, edi)        ; After (matches GCC)   
    xor edi, edi                          xor esi, esi
    xor esi, esi                          jmp fdecl_0@<!-- -->PLT                                                 
    jmp fdecl_0@<!-- -->PLT

[Added new lit testcases at `Transforms/CorrelatedValuePropagation/call-arg-known-param.ll` covering basic zero, non-zero constant, type mismatch, and two-parameter cases.

---
Full diff: https://github.com/llvm/llvm-project/pull/196715.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp (+79-49) 
- (added) llvm/test/Transforms/CorrelatedValuePropagation/call-arg-known-param.ll (+82) 


``````````diff
diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
index ff0b70b51e5f7..0ef1bd2cb07f1 100644
--- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
+++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
@@ -49,43 +49,43 @@ using namespace llvm;
 
 #define DEBUG_TYPE "correlated-value-propagation"
 
-STATISTIC(NumPhis,      "Number of phis propagated");
+STATISTIC(NumPhis, "Number of phis propagated");
 STATISTIC(NumPhiCommon, "Number of phis deleted via common incoming value");
-STATISTIC(NumSelects,   "Number of selects propagated");
-STATISTIC(NumCmps,      "Number of comparisons propagated");
-STATISTIC(NumReturns,   "Number of return values propagated");
+STATISTIC(NumSelects, "Number of selects propagated");
+STATISTIC(NumCmps, "Number of comparisons propagated");
+STATISTIC(NumReturns, "Number of return values propagated");
 STATISTIC(NumDeadCases, "Number of switch cases removed");
 STATISTIC(NumSDivSRemsNarrowed,
           "Number of sdivs/srems whose width was decreased");
-STATISTIC(NumSDivs,     "Number of sdiv converted to udiv");
+STATISTIC(NumSDivs, "Number of sdiv converted to udiv");
 STATISTIC(NumUDivURemsNarrowed,
           "Number of udivs/urems whose width was decreased");
 STATISTIC(NumAShrsConverted, "Number of ashr converted to lshr");
 STATISTIC(NumAShrsRemoved, "Number of ashr removed");
-STATISTIC(NumSRems,     "Number of srem converted to urem");
-STATISTIC(NumSExt,      "Number of sext converted to zext");
-STATISTIC(NumSIToFP,    "Number of sitofp converted to uitofp");
-STATISTIC(NumSICmps,    "Number of signed icmp preds simplified to unsigned");
-STATISTIC(NumAnd,       "Number of ands removed");
-STATISTIC(NumNW,        "Number of no-wrap deductions");
-STATISTIC(NumNSW,       "Number of no-signed-wrap deductions");
-STATISTIC(NumNUW,       "Number of no-unsigned-wrap deductions");
-STATISTIC(NumAddNW,     "Number of no-wrap deductions for add");
-STATISTIC(NumAddNSW,    "Number of no-signed-wrap deductions for add");
-STATISTIC(NumAddNUW,    "Number of no-unsigned-wrap deductions for add");
-STATISTIC(NumSubNW,     "Number of no-wrap deductions for sub");
-STATISTIC(NumSubNSW,    "Number of no-signed-wrap deductions for sub");
-STATISTIC(NumSubNUW,    "Number of no-unsigned-wrap deductions for sub");
-STATISTIC(NumMulNW,     "Number of no-wrap deductions for mul");
-STATISTIC(NumMulNSW,    "Number of no-signed-wrap deductions for mul");
-STATISTIC(NumMulNUW,    "Number of no-unsigned-wrap deductions for mul");
-STATISTIC(NumShlNW,     "Number of no-wrap deductions for shl");
-STATISTIC(NumShlNSW,    "Number of no-signed-wrap deductions for shl");
-STATISTIC(NumShlNUW,    "Number of no-unsigned-wrap deductions for shl");
-STATISTIC(NumAbs,       "Number of llvm.abs intrinsics removed");
+STATISTIC(NumSRems, "Number of srem converted to urem");
+STATISTIC(NumSExt, "Number of sext converted to zext");
+STATISTIC(NumSIToFP, "Number of sitofp converted to uitofp");
+STATISTIC(NumSICmps, "Number of signed icmp preds simplified to unsigned");
+STATISTIC(NumAnd, "Number of ands removed");
+STATISTIC(NumNW, "Number of no-wrap deductions");
+STATISTIC(NumNSW, "Number of no-signed-wrap deductions");
+STATISTIC(NumNUW, "Number of no-unsigned-wrap deductions");
+STATISTIC(NumAddNW, "Number of no-wrap deductions for add");
+STATISTIC(NumAddNSW, "Number of no-signed-wrap deductions for add");
+STATISTIC(NumAddNUW, "Number of no-unsigned-wrap deductions for add");
+STATISTIC(NumSubNW, "Number of no-wrap deductions for sub");
+STATISTIC(NumSubNSW, "Number of no-signed-wrap deductions for sub");
+STATISTIC(NumSubNUW, "Number of no-unsigned-wrap deductions for sub");
+STATISTIC(NumMulNW, "Number of no-wrap deductions for mul");
+STATISTIC(NumMulNSW, "Number of no-signed-wrap deductions for mul");
+STATISTIC(NumMulNUW, "Number of no-unsigned-wrap deductions for mul");
+STATISTIC(NumShlNW, "Number of no-wrap deductions for shl");
+STATISTIC(NumShlNSW, "Number of no-signed-wrap deductions for shl");
+STATISTIC(NumShlNUW, "Number of no-unsigned-wrap deductions for shl");
+STATISTIC(NumAbs, "Number of llvm.abs intrinsics removed");
 STATISTIC(NumOverflows, "Number of overflow checks removed");
 STATISTIC(NumSaturating,
-    "Number of saturating arithmetics converted to normal arithmetics");
+          "Number of saturating arithmetics converted to normal arithmetics");
 STATISTIC(NumNonNull, "Number of function pointer arguments marked non-null");
 STATISTIC(NumCmpIntr, "Number of llvm.[us]cmp intrinsics removed");
 STATISTIC(NumMinMax, "Number of llvm.[us]{min,max} intrinsics removed");
@@ -94,6 +94,8 @@ STATISTIC(NumSMinMax,
 STATISTIC(NumUDivURemsNarrowedExpanded,
           "Number of bound udiv's/urem's expanded");
 STATISTIC(NumNNeg, "Number of zext/uitofp non-negative deductions");
+STATISTIC(NumCallArgReplacedWithParam,
+          "Number of call args replaced with known-equal function params");
 
 static Constant *getConstantAt(Value *V, Instruction *At, LazyValueInfo *LVI) {
   if (Constant *C = LVI->getConstant(V, At))
@@ -261,7 +263,8 @@ static bool processPHI(PHINode *P, LazyValueInfo *LVI, DominatorTree *DT,
   BasicBlock *BB = P->getParent();
   for (unsigned i = 0, e = P->getNumIncomingValues(); i < e; ++i) {
     Value *Incoming = P->getIncomingValue(i);
-    if (isa<Constant>(Incoming)) continue;
+    if (isa<Constant>(Incoming))
+      continue;
 
     Value *V = getValueOnEdge(LVI, Incoming, P->getIncomingBlock(i), BB, P);
     if (V) {
@@ -372,7 +375,7 @@ static bool processSwitch(SwitchInst *I, LazyValueInfo *LVI,
 
   // Analyse each switch case in turn.
   bool Changed = false;
-  DenseMap<BasicBlock*, int> SuccessorsCount;
+  DenseMap<BasicBlock *, int> SuccessorsCount;
   for (auto *Succ : successors(BB))
     SuccessorsCount[Succ]++;
 
@@ -409,8 +412,8 @@ static bool processSwitch(SwitchInst *I, LazyValueInfo *LVI,
         CI = SI.removeCase(CI);
         CE = SI->case_end();
 
-        // The condition can be modified by removePredecessor's PHI simplification
-        // logic.
+        // The condition can be modified by removePredecessor's PHI
+        // simplification logic.
         Cond = SI->getCondition();
 
         ++NumDeadCases;
@@ -646,9 +649,9 @@ static bool processOverflowIntrinsic(WithOverflowInst *WO, LazyValueInfo *LVI) {
   setDeducedOverflowingFlags(NewOp, Opcode, NSW, NUW);
 
   StructType *ST = cast<StructType>(WO->getType());
-  Constant *Struct = ConstantStruct::get(ST,
-      { PoisonValue::get(ST->getElementType(0)),
-        ConstantInt::getFalse(ST->getElementType(1)) });
+  Constant *Struct =
+      ConstantStruct::get(ST, {PoisonValue::get(ST->getElementType(0)),
+                               ConstantInt::getFalse(ST->getElementType(1))});
   Value *NewI = B.CreateInsertValue(Struct, NewOp, 0);
   WO->replaceAllUsesWith(NewI);
   WO->eraseFromParent();
@@ -716,13 +719,16 @@ static bool processCallSite(CallBase &CB, LazyValueInfo *LVI) {
   // we may have a conditional fact with which LVI can fold.
   if (auto DeoptBundle = CB.getOperandBundle(LLVMContext::OB_deopt)) {
     for (const Use &ConstU : DeoptBundle->Inputs) {
-      Use &U = const_cast<Use&>(ConstU);
+      Use &U = const_cast<Use &>(ConstU);
       Value *V = U.get();
-      if (V->getType()->isVectorTy()) continue;
-      if (isa<Constant>(V)) continue;
+      if (V->getType()->isVectorTy())
+        continue;
+      if (isa<Constant>(V))
+        continue;
 
       Constant *C = LVI->getConstant(V, &CB);
-      if (!C) continue;
+      if (!C)
+        continue;
       U.set(C);
       Changed = true;
     }
@@ -748,17 +754,39 @@ static bool processCallSite(CallBase &CB, LazyValueInfo *LVI) {
 
   assert(ArgNo == CB.arg_size() && "Call arguments not processed correctly.");
 
-  if (ArgNos.empty())
-    return Changed;
+  if (!ArgNos.empty()) {
+    NumNonNull += ArgNos.size();
+    AttributeList AS = CB.getAttributes();
+    LLVMContext &Ctx = CB.getContext();
+    AS = AS.addParamAttribute(Ctx, ArgNos,
+                              Attribute::get(Ctx, Attribute::NonNull));
+    CB.setAttributes(AS);
+    Changed = true;
+  }
 
-  NumNonNull += ArgNos.size();
-  AttributeList AS = CB.getAttributes();
-  LLVMContext &Ctx = CB.getContext();
-  AS = AS.addParamAttribute(Ctx, ArgNos,
-                            Attribute::get(Ctx, Attribute::NonNull));
-  CB.setAttributes(AS);
+  // If a call argument is a constant C, and a function parameter is known to
+  // equal C at this call site, replace C with the parameter. This lets the
+  // backend reuse the already-loaded register instead of materializing C again.
+  // Example: if (a == 0) foo(0) → if (a == 0) foo(a), saving "xor edi, edi".
+  Function *F = CB.getParent()->getParent();
+  for (unsigned I = 0, E = CB.arg_size(); I != E; ++I) {
+    auto *CI = dyn_cast<ConstantInt>(CB.getArgOperand(I));
+    if (!CI)
+      continue;
+    for (Argument &Param : F->args()) {
+      if (Param.getType() != CI->getType())
+        continue;
+      Constant *Known = LVI->getConstant(&Param, &CB);
+      if (Known != CI)
+        continue;
+      CB.setArgOperand(I, &Param);
+      ++NumCallArgReplacedWithParam;
+      Changed = true;
+      break;
+    }
+  }
 
-  return true;
+  return Changed;
 }
 
 enum class Domain { NonNegative, NonPositive, Unknown };
@@ -1341,13 +1369,15 @@ static bool runImpl(Function &F, LazyValueInfo *LVI, DominatorTree *DT,
       // simplify the writing of unit tests, but also helps to enable IPO by
       // constant folding the return values of callees.
       auto *RetVal = RI->getReturnValue();
-      if (!RetVal) break; // handle "ret void"
+      if (!RetVal)
+        break; // handle "ret void"
       if (RetRange && !RetRange->isFullSet())
         RetRange =
             RetRange->unionWith(LVI->getConstantRange(RetVal, RI,
                                                       /*UndefAllowed=*/false));
 
-      if (isa<Constant>(RetVal)) break; // nothing to do
+      if (isa<Constant>(RetVal))
+        break; // nothing to do
       if (auto *C = getConstantAt(RetVal, RI, LVI)) {
         ++NumReturns;
         RI->replaceUsesOfWith(RetVal, C);
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/call-arg-known-param.ll b/llvm/test/Transforms/CorrelatedValuePropagation/call-arg-known-param.ll
new file mode 100644
index 0000000000000..511e127796b20
--- /dev/null
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/call-arg-known-param.ll
@@ -0,0 +1,82 @@
+; RUN: opt -passes=correlated-propagation -S < %s | FileCheck %s
+
+; When a call argument is a constant C and a function parameter is known to
+; equal C at the call site (due to a guarding icmp eq), replace C with the
+; parameter so the backend can reuse the already-loaded register.
+; Issue: https://github.com/llvm/llvm-project/issues/195907
+
+; Basic case: i32 0 replaced with %a (same type, known equal).
+; i64 0 is NOT replaced because %a is i32 (type mismatch).
+define void @test_basic(i32 %a) {
+; CHECK-LABEL: @test_basic(
+; CHECK:       if.then:
+; CHECK-NEXT:    call void @fdecl_0(i32 %a, i64 0)
+entry:
+  %cmp = icmp eq i32 %a, 0
+  br i1 %cmp, label %if.then, label %if.end
+if.then:
+  call void @fdecl_0(i32 0, i64 0)
+  br label %if.end
+if.end:
+  ret void
+}
+
+; Non-zero constant: %a == 5, so 5 is replaced with %a.
+define void @test_nonzero(i32 %a) {
+; CHECK-LABEL: @test_nonzero(
+; CHECK:       if.then:
+; CHECK-NEXT:    call void @fdecl_i32(i32 %a)
+entry:
+  %cmp = icmp eq i32 %a, 5
+  br i1 %cmp, label %if.then, label %if.end
+if.then:
+  call void @fdecl_i32(i32 5)
+  br label %if.end
+if.end:
+  ret void
+}
+
+; Negative: no condition guards the constant -- leave it alone.
+define void @test_no_condition(i32 %a) {
+; CHECK-LABEL: @test_no_condition(
+; CHECK:    call void @fdecl_i32(i32 0)
+  call void @fdecl_i32(i32 0)
+  ret void
+}
+
+; Negative: type mismatch -- %a is i32 but call arg is i64.
+define void @test_type_mismatch(i32 %a) {
+; CHECK-LABEL: @test_type_mismatch(
+; CHECK:       if.then:
+; CHECK-NEXT:    call void @fdecl_i64(i64 0)
+entry:
+  %cmp = icmp eq i32 %a, 0
+  br i1 %cmp, label %if.then, label %if.end
+if.then:
+  call void @fdecl_i64(i64 0)
+  br label %if.end
+if.end:
+  ret void
+}
+
+; Two params: both replaceable when the branch depends on both being equal.
+define void @test_two_params(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_two_params(
+; CHECK:       if.then:
+; CHECK-NEXT:    call void @fdecl_two_i32(i32 %a, i32 %b)
+entry:
+  %cmp_a = icmp eq i32 %a, 1
+  %cmp_b = icmp eq i32 %b, 2
+  %and = and i1 %cmp_a, %cmp_b
+  br i1 %and, label %if.then, label %if.end
+if.then:
+  call void @fdecl_two_i32(i32 1, i32 2)
+  br label %if.end
+if.end:
+  ret void
+}
+
+declare void @fdecl_0(i32, i64)
+declare void @fdecl_i32(i32)
+declare void @fdecl_i64(i64)
+declare void @fdecl_two_i32(i32, i32)
\ No newline at end of file

``````````

</details>


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


More information about the llvm-commits mailing list