[llvm] [VPlan] Add VPIRAttributes; use for VPWidenCallRecipe. (PR #207075)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 16 14:02:45 PDT 2026
https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/207075
>From dff0a23666dc5094c21dea7bcf43cbb0bc2d039b Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Tue, 2 Jun 2026 14:35:51 +0100
Subject: [PATCH 1/2] [VPlan] Add VPIRAttributes; use for VPWidenCallRecipe.
Add a VPIRAttributes mixin (modeled after VPIRMetadata) that stores an
AttributeList for a recipe's underlying call. At construction,
attributes that can safely by used are added (currently non-UB implying
ones like Range, Alignmen, NonNull, FPClass). UB-implying attributes
would be problematic when folding the tail masks off lanes.
We also make sure the attribute is compatible with the widened type.
---
llvm/lib/Transforms/Vectorize/VPlan.h | 38 +++++-
.../lib/Transforms/Vectorize/VPlanRecipes.cpp | 109 +++++++++++++++++-
.../Transforms/Vectorize/VPlanTransforms.cpp | 5 +-
.../AArch64/widen-call-attrs-masked.ll | 4 +-
.../LoopVectorize/VPlan/print-attributes.ll | 72 +++++++++++-
.../widen-call-attrs-interleave.ll | 4 +-
.../LoopVectorize/widen-call-attrs.ll | 18 +--
7 files changed, 227 insertions(+), 23 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h
index eaf9d1433aff7..670ee6fa3dbba 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.h
+++ b/llvm/lib/Transforms/Vectorize/VPlan.h
@@ -35,6 +35,7 @@
#include "llvm/Analysis/IVDescriptors.h"
#include "llvm/Analysis/MemoryLocation.h"
#include "llvm/Analysis/VectorUtils.h"
+#include "llvm/IR/Attributes.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/FMF.h"
#include "llvm/IR/Operator.h"
@@ -1216,6 +1217,33 @@ class LLVM_ABI_FOR_TEST VPIRMetadata {
#endif
};
+/// Manage IR per-argument and return attributes for call recipes. Only
+/// attributes that are valid to propagate to the widened call are stored.
+class VPIRAttributes {
+ AttributeList CallAttrs;
+
+public:
+ VPIRAttributes() = default;
+
+ /// Stores the per-arg/return attributes from \p CI that are valid to
+ /// propagate to a widened call to \p Variant.
+ VPIRAttributes(CallInst &CI, const Function &Variant);
+
+ VPIRAttributes(const VPIRAttributes &Other) = default;
+ VPIRAttributes &operator=(const VPIRAttributes &Other) = default;
+
+ /// Apply the stored per-arg/return attributes to widened call \p V.
+ void applyAttrs(CallInst &V) const;
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ /// Print return-value attributes carried with this object.
+ void printRetAttrs(raw_ostream &O) const;
+
+ /// Print per-arg attributes for argument \p ArgIdx.
+ void printParamAttrs(raw_ostream &O, unsigned ArgIdx) const;
+#endif
+};
+
/// This is a concrete Recipe that models a single VPlan-level instruction.
/// While as any Recipe it may generate a sequence of IR instructions when
/// executed, these instructions would always form a single-def expression as
@@ -2094,7 +2122,8 @@ class VPWidenMemIntrinsicRecipe final : public VPWidenIntrinsicRecipe {
/// A recipe for widening Call instructions using library calls.
class LLVM_ABI_FOR_TEST VPWidenCallRecipe : public VPRecipeWithIRFlags,
- public VPIRMetadata {
+ public VPIRMetadata,
+ public VPIRAttributes {
/// Variant stores a pointer to the chosen function. There is a 1:1 mapping
/// between a given VF and the chosen vectorized variant, so there will be a
/// different VPlan for each VF with a valid variant.
@@ -2104,11 +2133,12 @@ class LLVM_ABI_FOR_TEST VPWidenCallRecipe : public VPRecipeWithIRFlags,
VPWidenCallRecipe(Value *UV, Function *Variant,
ArrayRef<VPValue *> CallArguments,
const VPIRFlags &Flags = {},
- const VPIRMetadata &Metadata = {}, DebugLoc DL = {})
+ const VPIRMetadata &Metadata = {},
+ const VPIRAttributes &Attrs = {}, DebugLoc DL = {})
: VPRecipeWithIRFlags(VPRecipeBase::VPWidenCallSC, CallArguments,
toScalarizedTy(Variant->getReturnType()), Flags,
DL),
- VPIRMetadata(Metadata), Variant(Variant) {
+ VPIRMetadata(Metadata), VPIRAttributes(Attrs), Variant(Variant) {
setUnderlyingValue(UV);
assert(
isa<Function>(getOperand(getNumOperands() - 1)->getLiveInIRValue()) &&
@@ -2122,7 +2152,7 @@ class LLVM_ABI_FOR_TEST VPWidenCallRecipe : public VPRecipeWithIRFlags,
VPWidenCallRecipe *clone() override {
return new VPWidenCallRecipe(getUnderlyingValue(), Variant, operands(),
- *this, *this, getDebugLoc());
+ *this, *this, *this, getDebugLoc());
}
VP_CLASSOF_IMPL(VPRecipeBase::VPWidenCallSC)
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index f45b9e4f6c35b..fa48ae31e8b1b 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -24,6 +24,8 @@
#include "llvm/Analysis/IVDescriptors.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
+#include "llvm/IR/AttributeMask.h"
+#include "llvm/IR/Attributes.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instruction.h"
@@ -2121,6 +2123,101 @@ void VPIRMetadata::print(raw_ostream &O, VPSlotTracker &SlotTracker) const {
}
#endif
+VPIRAttributes::VPIRAttributes(CallInst &CI, const Function &Variant) {
+ LLVMContext &Ctx = CI.getContext();
+ AttributeList ScalarAttrs = CI.getAttributes();
+ AttributeList VariantAttrs = Variant.getAttributes();
+ FunctionType *VariantTy = Variant.getFunctionType();
+
+ static constexpr Attribute::AttrKind Allowed[] = {
+ Attribute::Alignment, Attribute::NonNull, Attribute::NoFPClass,
+ Attribute::Range};
+
+ // Combine attributes from the Variant declaration and the call site, picking
+ // the stricter one.
+ auto CombineAttrs = [&](Attribute CallAttr,
+ Attribute VariantAttr) -> Attribute {
+ if (!VariantAttr.isValid())
+ return CallAttr;
+ switch (CallAttr.getKindAsEnum()) {
+ case Attribute::Alignment:
+ return Attribute::getWithAlignment(
+ Ctx, std::max(CallAttr.getAlignment().valueOrOne(),
+ VariantAttr.getAlignment().valueOrOne()));
+ case Attribute::NoFPClass:
+ return Attribute::getWithNoFPClass(Ctx, CallAttr.getNoFPClass() |
+ VariantAttr.getNoFPClass());
+ case Attribute::Range:
+ return Attribute::get(
+ Ctx, Attribute::Range,
+ CallAttr.getRange().intersectWith(VariantAttr.getRange()));
+ default:
+ // Boolean attributes (e.g. nonnull) carry no value to combine.
+ return CallAttr;
+ }
+ };
+
+ // Only keep allowed attributes (which do not trigger UB) and ones that are
+ // compatible with the widened type of Variant.
+ auto Filter = [&](Type *Ty, AttributeSet CallAS, AttributeSet VariantAS) {
+ AttributeMask Incompatible =
+ AttributeFuncs::typeIncompatible(Ty, CallAS, AttributeFuncs::ASK_ALL);
+ AttrBuilder AB(Ctx);
+ for (Attribute::AttrKind K : Allowed) {
+ if (CallAS.hasAttribute(K) && !Incompatible.contains(K))
+ AB.addAttribute(
+ CombineAttrs(CallAS.getAttribute(K), VariantAS.getAttribute(K)));
+ }
+ return AttributeSet::get(Ctx, AB);
+ };
+
+ AttributeSet RetAttrs;
+ if (!Variant.getReturnType()->isVoidTy())
+ RetAttrs = Filter(Variant.getReturnType(), ScalarAttrs.getRetAttrs(),
+ VariantAttrs.getRetAttrs());
+
+ SmallVector<AttributeSet> ArgAttrs(VariantTy->getNumParams());
+ for (unsigned I = 0, E = std::min<unsigned>(CI.arg_size(),
+ VariantTy->getNumParams());
+ I != E; ++I) {
+ AttributeSet PA = ScalarAttrs.getParamAttrs(I);
+ if (!PA.hasAttributes())
+ continue;
+ ArgAttrs[I] =
+ Filter(VariantTy->getParamType(I), PA, VariantAttrs.getParamAttrs(I));
+ }
+
+ CallAttrs = AttributeList::get(Ctx, AttributeSet(), RetAttrs, ArgAttrs);
+}
+
+void VPIRAttributes::applyAttrs(CallInst &V) const {
+ LLVMContext &Ctx = V.getContext();
+
+ AttributeSet RetAttrs = CallAttrs.getRetAttrs();
+ if (RetAttrs.hasAttributes())
+ V.addRetAttrs(AttrBuilder(Ctx, RetAttrs));
+
+ for (unsigned I = 0, E = V.arg_size(); I != E; ++I) {
+ AttributeSet PA = CallAttrs.getParamAttrs(I);
+ if (PA.hasAttributes())
+ V.addParamAttrs(I, AttrBuilder(Ctx, PA));
+ }
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+void VPIRAttributes::printRetAttrs(raw_ostream &O) const {
+ AttributeSet RetAttrs = CallAttrs.getRetAttrs();
+ if (RetAttrs.hasAttributes())
+ O << " " << RetAttrs.getAsString();
+}
+
+void VPIRAttributes::printParamAttrs(raw_ostream &O, unsigned ArgIdx) const {
+ AttributeSet PA = CallAttrs.getParamAttrs(ArgIdx);
+ if (PA.hasAttributes())
+ O << PA.getAsString() << " ";
+}
+#endif
+
void VPWidenCallRecipe::execute(VPTransformState &State) {
assert(State.VF.isVector() && "not widening");
assert(Variant != nullptr && "Can't create vector function.");
@@ -2150,6 +2247,8 @@ void VPWidenCallRecipe::execute(VPTransformState &State) {
applyMetadata(*V);
V->setCallingConv(Variant->getCallingConv());
+ applyAttrs(*V);
+
if (!V->getType()->isVoidTy())
State.set(this, V);
}
@@ -2195,10 +2294,14 @@ void VPWidenCallRecipe::printRecipe(raw_ostream &O, const Twine &Indent,
O << "call";
printFlags(O);
+ printRetAttrs(O);
O << " @" << CalledFn->getName() << "(";
- interleaveComma(args(), O, [&O, &SlotTracker](VPValue *Op) {
- Op->printAsOperand(O, SlotTracker);
- });
+ interleaveComma(enumerate(args()), O,
+ [&O, &SlotTracker, this](const auto &IndexedOp) {
+ auto [Idx, Op] = IndexedOp;
+ printParamAttrs(O, Idx);
+ Op->printAsOperand(O, SlotTracker);
+ });
O << ")";
O << " (using library function";
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index ceb7c38ca3e29..bf238735bb7d8 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -7497,8 +7497,9 @@ void VPlanTransforms::makeCallWideningDecisions(VPlan &Plan, VFRange &Range,
Ops.push_back(Mask);
}
Ops.push_back(VPI->getOperand(VPI->getNumOperandsWithoutMask() - 1));
- Replacement = new VPWidenCallRecipe(CI, Decision.Variant, Ops, *VPI,
- *VPI, VPI->getDebugLoc());
+ Replacement = new VPWidenCallRecipe(
+ CI, Decision.Variant, Ops, *VPI, *VPI,
+ VPIRAttributes(*CI, *Decision.Variant), VPI->getDebugLoc());
break;
}
case CallWideningDecision::KindTy::Scalarize:
diff --git a/llvm/test/Transforms/LoopVectorize/AArch64/widen-call-attrs-masked.ll b/llvm/test/Transforms/LoopVectorize/AArch64/widen-call-attrs-masked.ll
index 24d85bfdea0b5..d7e04494fd4ee 100644
--- a/llvm/test/Transforms/LoopVectorize/AArch64/widen-call-attrs-masked.ll
+++ b/llvm/test/Transforms/LoopVectorize/AArch64/widen-call-attrs-masked.ll
@@ -25,7 +25,7 @@ define void @masked_range_arg_and_ret_propagated(ptr noalias %a, ptr readnone %b
; CHECK-NEXT: [[ACTIVE_LANE_MASK:%.*]] = phi <vscale x 2 x i1> [ [[ACTIVE_LANE_MASK_ENTRY]], %[[VECTOR_PH]] ], [ [[ACTIVE_LANE_MASK_NEXT:%.*]], %[[VECTOR_BODY]] ]
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr i64, ptr [[B]], i64 [[INDEX]]
; CHECK-NEXT: [[WIDE_MASKED_LOAD:%.*]] = call <vscale x 2 x i64> @llvm.masked.load.nxv2i64.p0(ptr align 8 [[TMP2]], <vscale x 2 x i1> [[ACTIVE_LANE_MASK]], <vscale x 2 x i64> poison)
-; CHECK-NEXT: [[TMP3:%.*]] = call <vscale x 2 x i64> @foo_vector(<vscale x 2 x i64> [[WIDE_MASKED_LOAD]], <vscale x 2 x i1> [[ACTIVE_LANE_MASK]])
+; CHECK-NEXT: [[TMP3:%.*]] = call range(i64 0, 100) <vscale x 2 x i64> @foo_vector(<vscale x 2 x i64> range(i64 0, 50) [[WIDE_MASKED_LOAD]], <vscale x 2 x i1> [[ACTIVE_LANE_MASK]])
; CHECK-NEXT: [[TMP4:%.*]] = getelementptr i64, ptr [[A]], i64 [[INDEX]]
; CHECK-NEXT: call void @llvm.masked.store.nxv2i64.p0(<vscale x 2 x i64> [[TMP3]], ptr align 8 [[TMP4]], <vscale x 2 x i1> [[ACTIVE_LANE_MASK]])
; CHECK-NEXT: [[INDEX_NEXT]] = add i64 [[INDEX]], [[TMP1]]
@@ -72,7 +72,7 @@ define void @masked_noundef_dereferenceable_stripped(ptr noalias %a, ptr readnon
; CHECK-NEXT: [[ACTIVE_LANE_MASK:%.*]] = phi <vscale x 2 x i1> [ [[ACTIVE_LANE_MASK_ENTRY]], %[[VECTOR_PH]] ], [ [[ACTIVE_LANE_MASK_NEXT:%.*]], %[[VECTOR_BODY]] ]
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr i64, ptr [[B]], i64 [[INDEX]]
; CHECK-NEXT: [[WIDE_MASKED_LOAD:%.*]] = call <vscale x 2 x i64> @llvm.masked.load.nxv2i64.p0(ptr align 8 [[TMP2]], <vscale x 2 x i1> [[ACTIVE_LANE_MASK]], <vscale x 2 x i64> poison)
-; CHECK-NEXT: [[TMP3:%.*]] = call <vscale x 2 x i64> @foo_vector(<vscale x 2 x i64> [[WIDE_MASKED_LOAD]], <vscale x 2 x i1> [[ACTIVE_LANE_MASK]])
+; CHECK-NEXT: [[TMP3:%.*]] = call range(i64 0, 100) <vscale x 2 x i64> @foo_vector(<vscale x 2 x i64> range(i64 0, 50) [[WIDE_MASKED_LOAD]], <vscale x 2 x i1> [[ACTIVE_LANE_MASK]])
; CHECK-NEXT: [[TMP4:%.*]] = getelementptr i64, ptr [[A]], i64 [[INDEX]]
; CHECK-NEXT: call void @llvm.masked.store.nxv2i64.p0(<vscale x 2 x i64> [[TMP3]], ptr align 8 [[TMP4]], <vscale x 2 x i1> [[ACTIVE_LANE_MASK]])
; CHECK-NEXT: [[INDEX_NEXT]] = add i64 [[INDEX]], [[TMP1]]
diff --git a/llvm/test/Transforms/LoopVectorize/VPlan/print-attributes.ll b/llvm/test/Transforms/LoopVectorize/VPlan/print-attributes.ll
index d7f46b8a62a5a..791cc20a7447e 100644
--- a/llvm/test/Transforms/LoopVectorize/VPlan/print-attributes.ll
+++ b/llvm/test/Transforms/LoopVectorize/VPlan/print-attributes.ll
@@ -6,6 +6,9 @@ target triple = "arm64-apple-macosx"
declare double @acos(double)
declare <2 x double> @vec_acos(<2 x double>)
+declare i64 @bar(i64)
+declare range(i64 0, 60) <2 x i64> @vec_bar(<2 x i64> range(i64 0, 50))
+
define void @wide_call_attrs(ptr noalias %in.ptr, ptr noalias %out.ptr) {
; CHECK-LABEL: VPlan for loop in 'wide_call_attrs'
; CHECK: VPlan ' for UF>=1' {
@@ -28,7 +31,7 @@ define void @wide_call_attrs(ptr noalias %in.ptr, ptr noalias %out.ptr) {
; CHECK-NEXT: CLONE ir<%in.gep> = getelementptr inbounds ir<%in.ptr>, ir<%iv>
; CHECK-NEXT: vp<[[VP4:%[0-9]+]]> = vector-pointer inbounds ir<%in.gep>, ir<1>
; CHECK-NEXT: WIDEN ir<%in> = load vp<[[VP4]]>
-; CHECK-NEXT: WIDEN-CALL ir<%call> = call @acos(ir<%in>) (using library function: vec_acos)
+; CHECK-NEXT: WIDEN-CALL ir<%call> = call nofpclass(nan) @acos(nofpclass(inf) ir<%in>) (using library function: vec_acos)
; CHECK-NEXT: CLONE ir<%out.gep> = getelementptr inbounds ir<%out.ptr>, ir<%iv>
; CHECK-NEXT: vp<[[VP5:%[0-9]+]]> = vector-pointer inbounds ir<%out.gep>, ir<1>
; CHECK-NEXT: WIDEN store vp<[[VP5]]>, ir<%call>
@@ -69,6 +72,72 @@ exit:
ret void
}
+; The call site carries a propagatable (range) and a non-propagatable
+; (noundef) attribute on both the argument and return. Make sure only the valid
+; attributes are printed.
+define void @wide_call_attrs_filtered_and_combined(ptr noalias %in.ptr, ptr noalias %out.ptr) {
+; CHECK-LABEL: VPlan for loop in 'wide_call_attrs_filtered_and_combined'
+; CHECK: VPlan ' for UF>=1' {
+; CHECK-NEXT: Live-in vp<[[VP0:%[0-9]+]]> = VF
+; CHECK-NEXT: Live-in vp<[[VP1:%[0-9]+]]> = VF * UF
+; CHECK-NEXT: Live-in vp<[[VP2:%[0-9]+]]> = vector-trip-count
+; CHECK-NEXT: Live-in ir<1000> = original trip-count
+; CHECK-EMPTY:
+; CHECK-NEXT: ir-bb<entry>:
+; CHECK-NEXT: Successor(s): scalar.ph, vector.ph
+; CHECK-EMPTY:
+; CHECK-NEXT: vector.ph:
+; CHECK-NEXT: Successor(s): vector loop
+; CHECK-EMPTY:
+; CHECK-NEXT: <x1> vector loop: {
+; CHECK-NEXT: vp<[[VP3:%[0-9]+]]> = CANONICAL-IV
+; CHECK-EMPTY:
+; CHECK-NEXT: vector.body:
+; CHECK-NEXT: ir<%iv> = WIDEN-INDUCTION nuw nsw ir<0>, ir<1>, vp<[[VP0]]>
+; CHECK-NEXT: CLONE ir<%in.gep> = getelementptr inbounds ir<%in.ptr>, ir<%iv>
+; CHECK-NEXT: vp<[[VP4:%[0-9]+]]> = vector-pointer inbounds ir<%in.gep>, ir<1>
+; CHECK-NEXT: WIDEN ir<%in> = load vp<[[VP4]]>
+; CHECK-NEXT: WIDEN-CALL ir<%call> = call range(i64 0, 60) @bar(range(i64 0, 50) ir<%in>) (using library function: vec_bar)
+; CHECK-NEXT: CLONE ir<%out.gep> = getelementptr inbounds ir<%out.ptr>, ir<%iv>
+; CHECK-NEXT: vp<[[VP5:%[0-9]+]]> = vector-pointer inbounds ir<%out.gep>, ir<1>
+; CHECK-NEXT: WIDEN store vp<[[VP5]]>, ir<%call>
+; CHECK-NEXT: EMIT ir<%iv.next> = add nuw nsw ir<%iv>, ir<1>
+; CHECK-NEXT: CLONE ir<%exitcond> = icmp eq ir<%iv.next>, ir<1000>
+; CHECK-NEXT: EMIT vp<%index.next> = add nuw vp<[[VP3]]>, vp<[[VP1]]>
+; CHECK-NEXT: EMIT branch-on-count vp<%index.next>, vp<[[VP2]]>
+; CHECK-NEXT: No successors
+; CHECK-NEXT: }
+; CHECK-NEXT: Successor(s): middle.block
+; CHECK-EMPTY:
+; CHECK-NEXT: middle.block:
+; CHECK-NEXT: EMIT vp<[[VP7:%[0-9]+]]> = exiting-iv-value ir<%iv>
+; CHECK-NEXT: EMIT vp<%cmp.n> = icmp eq ir<1000>, vp<[[VP2]]>
+; CHECK-NEXT: EMIT branch-on-cond vp<%cmp.n>
+; CHECK-NEXT: Successor(s): ir-bb<exit>, scalar.ph
+; CHECK-EMPTY:
+; CHECK-NEXT: ir-bb<exit>:
+; CHECK-NEXT: No successors
+; CHECK-EMPTY:
+; CHECK-NEXT: scalar.ph:
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %in.gep = getelementptr inbounds i64, ptr %in.ptr, i64 %iv
+ %in = load i64, ptr %in.gep, align 8
+ %call = tail call range(i64 0, 100) noundef i64 @bar(i64 range(i64 0, 80) noundef %in) #1
+ %out.gep = getelementptr inbounds i64, ptr %out.ptr, i64 %iv
+ store i64 %call, ptr %out.gep, align 8
+ %iv.next = add nuw nsw i64 %iv, 1
+ %exitcond = icmp eq i64 %iv.next, 1000
+ br i1 %exitcond, label %exit, label %loop
+
+exit:
+ ret void
+}
+
define void @test_intrinsic_with_arg_and_ret_attrs(ptr noalias %A, ptr noalias %B, i32 %n) {
; CHECK-LABEL: VPlan for loop in 'test_intrinsic_with_arg_and_ret_attrs'
; CHECK: VPlan ' for UF>=1' {
@@ -136,3 +205,4 @@ exit:
}
attributes #0 = { "vector-function-abi-variant"="_ZGVnN2v_acos(vec_acos)" }
+attributes #1 = { "vector-function-abi-variant"="_ZGVnN2v_bar(vec_bar)" }
diff --git a/llvm/test/Transforms/LoopVectorize/widen-call-attrs-interleave.ll b/llvm/test/Transforms/LoopVectorize/widen-call-attrs-interleave.ll
index b68f1545aad6a..2d19e2ae74b19 100644
--- a/llvm/test/Transforms/LoopVectorize/widen-call-attrs-interleave.ll
+++ b/llvm/test/Transforms/LoopVectorize/widen-call-attrs-interleave.ll
@@ -17,8 +17,8 @@ define void @range_propagated_uf2(ptr noalias %src, ptr noalias %out) {
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[TMP0]], i64 2
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <2 x i32>, ptr [[TMP0]], align 4
; CHECK-NEXT: [[WIDE_LOAD1:%.*]] = load <2 x i32>, ptr [[TMP1]], align 4
-; CHECK-NEXT: [[TMP2:%.*]] = call <2 x i32> @vec_fn_int(<2 x i32> [[WIDE_LOAD]])
-; CHECK-NEXT: [[TMP3:%.*]] = call <2 x i32> @vec_fn_int(<2 x i32> [[WIDE_LOAD1]])
+; CHECK-NEXT: [[TMP2:%.*]] = call <2 x i32> @vec_fn_int(<2 x i32> range(i32 0, 100) [[WIDE_LOAD]])
+; CHECK-NEXT: [[TMP3:%.*]] = call <2 x i32> @vec_fn_int(<2 x i32> range(i32 0, 100) [[WIDE_LOAD1]])
; CHECK-NEXT: [[TMP4:%.*]] = getelementptr i32, ptr [[OUT]], i64 [[INDEX]]
; CHECK-NEXT: [[TMP5:%.*]] = getelementptr i32, ptr [[TMP4]], i64 2
; CHECK-NEXT: store <2 x i32> [[TMP2]], ptr [[TMP4]], align 4
diff --git a/llvm/test/Transforms/LoopVectorize/widen-call-attrs.ll b/llvm/test/Transforms/LoopVectorize/widen-call-attrs.ll
index 071eb9b001027..0ec410b87bdd0 100644
--- a/llvm/test/Transforms/LoopVectorize/widen-call-attrs.ll
+++ b/llvm/test/Transforms/LoopVectorize/widen-call-attrs.ll
@@ -157,7 +157,7 @@ define void @range_arg_propagated(ptr noalias %src, ptr noalias %out) {
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[INDEX]]
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <2 x i32>, ptr [[TMP0]], align 4
-; CHECK-NEXT: [[TMP1:%.*]] = call <2 x i32> @vec_fn_int(<2 x i32> [[WIDE_LOAD]])
+; CHECK-NEXT: [[TMP1:%.*]] = call <2 x i32> @vec_fn_int(<2 x i32> range(i32 0, 100) [[WIDE_LOAD]])
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[OUT]], i64 [[INDEX]]
; CHECK-NEXT: store <2 x i32> [[TMP1]], ptr [[TMP2]], align 4
; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2
@@ -198,7 +198,7 @@ define void @nofpclass_propagated(ptr noalias %src, ptr noalias %out) {
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr float, ptr [[SRC]], i64 [[INDEX]]
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <2 x float>, ptr [[TMP0]], align 4
-; CHECK-NEXT: [[TMP1:%.*]] = call <2 x float> @vec_fn_fp(<2 x float> [[WIDE_LOAD]])
+; CHECK-NEXT: [[TMP1:%.*]] = call nofpclass(nan) <2 x float> @vec_fn_fp(<2 x float> nofpclass(nan) [[WIDE_LOAD]])
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr float, ptr [[OUT]], i64 [[INDEX]]
; CHECK-NEXT: store <2 x float> [[TMP1]], ptr [[TMP2]], align 4
; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2
@@ -234,7 +234,7 @@ define void @align_uniform_ptr_propagated(ptr noalias %p, ptr noalias %out) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br label %[[VECTOR_PH:.*]]
; CHECK: [[VECTOR_PH]]:
-; CHECK-NEXT: [[TMP0:%.*]] = call <2 x i32> @vec_fn_uniform_ptr(ptr [[P]])
+; CHECK-NEXT: [[TMP0:%.*]] = call <2 x i32> @vec_fn_uniform_ptr(ptr align 16 [[P]])
; CHECK-NEXT: br label %[[VECTOR_BODY:.*]]
; CHECK: [[VECTOR_BODY]]:
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
@@ -271,7 +271,7 @@ define void @nonnull_uniform_ptr_propagated(ptr noalias %p, ptr noalias %out) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br label %[[VECTOR_PH:.*]]
; CHECK: [[VECTOR_PH]]:
-; CHECK-NEXT: [[TMP0:%.*]] = call <2 x i32> @vec_fn_uniform_ptr(ptr [[P]])
+; CHECK-NEXT: [[TMP0:%.*]] = call <2 x i32> @vec_fn_uniform_ptr(ptr nonnull [[P]])
; CHECK-NEXT: br label %[[VECTOR_BODY:.*]]
; CHECK: [[VECTOR_BODY]]:
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
@@ -353,7 +353,7 @@ define void @range_arg_variant_decl_wins(ptr noalias %src, ptr noalias %out) {
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[INDEX]]
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <2 x i32>, ptr [[TMP0]], align 4
-; CHECK-NEXT: [[TMP1:%.*]] = call <2 x i32> @vec_fn_int_variant_attrs(<2 x i32> [[WIDE_LOAD]])
+; CHECK-NEXT: [[TMP1:%.*]] = call <2 x i32> @vec_fn_int_variant_attrs(<2 x i32> range(i32 0, 50) [[WIDE_LOAD]])
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[OUT]], i64 [[INDEX]]
; CHECK-NEXT: store <2 x i32> [[TMP1]], ptr [[TMP2]], align 4
; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2
@@ -391,7 +391,7 @@ define void @align_uniform_ptr_variant_decl_wins(ptr noalias %p, ptr noalias %ou
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br label %[[VECTOR_PH:.*]]
; CHECK: [[VECTOR_PH]]:
-; CHECK-NEXT: [[TMP0:%.*]] = call <2 x i32> @vec_fn_uniform_ptr_variant_attrs(ptr [[P]])
+; CHECK-NEXT: [[TMP0:%.*]] = call <2 x i32> @vec_fn_uniform_ptr_variant_attrs(ptr align 32 [[P]])
; CHECK-NEXT: br label %[[VECTOR_BODY:.*]]
; CHECK: [[VECTOR_BODY]]:
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
@@ -434,7 +434,7 @@ define void @nofpclass_arg_variant_decl_wins(ptr noalias %src, ptr noalias %out)
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr float, ptr [[SRC]], i64 [[INDEX]]
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <2 x float>, ptr [[TMP0]], align 4
-; CHECK-NEXT: [[TMP1:%.*]] = call <2 x float> @vec_fn_fp_variant_attrs(<2 x float> [[WIDE_LOAD]])
+; CHECK-NEXT: [[TMP1:%.*]] = call <2 x float> @vec_fn_fp_variant_attrs(<2 x float> nofpclass(nan inf) [[WIDE_LOAD]])
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr float, ptr [[OUT]], i64 [[INDEX]]
; CHECK-NEXT: store <2 x float> [[TMP1]], ptr [[TMP2]], align 4
; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2
@@ -475,7 +475,7 @@ define void @range_ret_propagated(ptr noalias %src, ptr noalias %out) {
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[INDEX]]
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <2 x i32>, ptr [[TMP0]], align 4
-; CHECK-NEXT: [[TMP1:%.*]] = call <2 x i32> @vec_fn_int(<2 x i32> [[WIDE_LOAD]])
+; CHECK-NEXT: [[TMP1:%.*]] = call range(i32 0, 100) <2 x i32> @vec_fn_int(<2 x i32> [[WIDE_LOAD]])
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[OUT]], i64 [[INDEX]]
; CHECK-NEXT: store <2 x i32> [[TMP1]], ptr [[TMP2]], align 4
; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2
@@ -516,7 +516,7 @@ define void @align_vec_ptr_propagated(ptr noalias %dst, ptr noalias %out) {
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr ptr, ptr [[DST]], i64 [[INDEX]]
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <2 x ptr>, ptr [[TMP0]], align 8
-; CHECK-NEXT: [[TMP1:%.*]] = call <2 x i32> @vec_fn_vec_ptr(<2 x ptr> [[WIDE_LOAD]])
+; CHECK-NEXT: [[TMP1:%.*]] = call <2 x i32> @vec_fn_vec_ptr(<2 x ptr> align 16 [[WIDE_LOAD]])
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[OUT]], i64 [[INDEX]]
; CHECK-NEXT: store <2 x i32> [[TMP1]], ptr [[TMP2]], align 4
; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2
>From 15b948cd2f43a754365a6659143137c2536bf460 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 16 Jul 2026 22:02:07 +0100
Subject: [PATCH 2/2] !fixup add TODO to support more attributes
---
llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index b703275d0367f..08b1f1088a63b 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -2148,6 +2148,7 @@ VPIRAttributes::VPIRAttributes(CallInst &CI, const Function &Variant) {
AttributeList VariantAttrs = Variant.getAttributes();
FunctionType *VariantTy = Variant.getFunctionType();
+ // TODO: Support additional attributes.
static constexpr Attribute::AttrKind Allowed[] = {
Attribute::Alignment, Attribute::NonNull, Attribute::NoFPClass,
Attribute::Range};
More information about the llvm-commits
mailing list