[llvm] [VPlan] Compute the cost for vector icmp and fcmp (PR #193268)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Fri May 1 06:16:29 PDT 2026


================
@@ -1257,6 +1257,15 @@ InstructionCost VPInstruction::computeCost(ElementCount VF,
     return Ctx.TTI.getIndexedVectorInstrCostFromEnd(Instruction::ExtractElement,
                                                     VecTy, Ctx.CostKind, 0);
   }
+  case Instruction::FCmp:
+  case Instruction::ICmp:
+    // FIXME: We don't handle scalar compares here yet. Scalar compares used for
+    // the loop exit condition are handled by the legacy cost model, but other
+    // scalar compares (e.g. in the middle block deciding whether to execute the
+    // scalar epilogue) aren't accounted for.
+    if (vputils::onlyFirstLaneUsed(this))
+      return 0;
----------------
fhahn wrote:

> the vector icmp/fcmp VPInstructions are introduced when creating the header mask for some tail-folding styles. If we change the logic to skip conditions outside the loop region, then that increases the scope of this PR to also cover scalar icmp/fcmp, which should probably live in a separate PR I think?

Right, my suggestion was to try to exclude the cases we do not want to handle outside the loop, to closer align with the comment (e.g. we could sell. It's fine to leave as-is for now,  but maybe trim the comment and just say that we only handle vector compares, as we also skip scalar compares in the loop.

Note that there are more places that create vector compares besides tail-folding masks. It looks like we still need a test case for the FCMP path, something like the below should cover it

```
  define double @fmaxnum_computed(double %base, i64 %n) {
  entry:
    br label %loop

  loop:
    %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
    %max = phi double [ -1.000000e+07, %entry ], [ %max.next, %loop ]
    %iv.f = sitofp i64 %iv to double
    %v = fadd double %base, %iv.f
    %max.next = call double @llvm.maxnum.f64(double %max, double %v)
    %iv.next = add nuw nsw i64 %iv, 1
    %ec = icmp eq i64 %iv.next, %n
    br i1 %ec, label %exit, label %loop

  exit:
    ret double %max.next
  }
```

Another interesting case that changes with the patch on AArch64 in real workloads is involving a switch. Would be good to also add this as new test case

```
target triple = "arm64-apple-macosx"

define i32 @foo(ptr %s, ptr %dst, i64 %n) {
entry:
  br label %loop.header

loop.header:
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %c = phi i32 [ 0, %entry ], [ %c.next, %loop.latch ]
  %gep = getelementptr i8, ptr %s, i64 %iv
  %l = load i8, ptr %gep
  switch i8 %l, label %loop.latch [
    i8 1, label %add.1
    i8 2, label %add.1
    i8 3, label %add.1
    i8 4, label %add.1
    i8 5, label %add.1
    i8 6, label %add.1
    i8 7, label %add.1
    i8 8, label %add.1
    i8 9, label %add.1
    i8 10, label %add.1
    i8 11, label %add.1
    i8 12, label %add.1
    i8 13, label %add.1
    i8 14, label %add.1
    i8 15, label %add.1
    i8 0, label %add.4
  ]

add.1:
  %c.1 = add nsw i32 %c, 1
  br label %loop.latch

add.4:
  %c.4 = add nsw i32 %c, 4
  br label %loop.latch

loop.latch:
  %c.next = phi i32 [ %c, %loop.header ], [ %c.1, %add.1 ], [ %c.4, %add.4 ]
  %dst.gep = getelementptr i8, ptr %dst, i64 %iv
  store i8 %l, ptr %dst.gep
  %iv.next = add nuw nsw i64 %iv, 1
  %ec = icmp eq i64 %iv.next, %n
  br i1 %ec, label %exit, label %loop.header

exit:
  ret i32 %c.next
}
```

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


More information about the llvm-commits mailing list