Index: llvm/include/llvm/Analysis/TargetTransformInfo.h =================================================================== --- llvm/include/llvm/Analysis/TargetTransformInfo.h +++ llvm/include/llvm/Analysis/TargetTransformInfo.h @@ -21,6 +21,7 @@ #ifndef LLVM_ANALYSIS_TARGETTRANSFORMINFO_H #define LLVM_ANALYSIS_TARGETTRANSFORMINFO_H +#include "llvm/Analysis/IVDescriptors.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Operator.h" #include "llvm/IR/PassManager.h" @@ -1305,6 +1306,9 @@ bool isLegalToVectorizeStoreChain(unsigned ChainSizeInBytes, Align Alignment, unsigned AddrSpace) const; + /// \returns True if it is legal to vectorize the given reduction kind. + bool isLegalToVectorizeReduction(RecurKind RecKind, bool Scalable) const; + /// \returns The new vector factor value if the target doesn't support \p /// SizeInBytes loads or has a better vector factor. unsigned getLoadVectorFactor(unsigned VF, unsigned LoadSize, @@ -1644,6 +1648,8 @@ virtual bool isLegalToVectorizeStoreChain(unsigned ChainSizeInBytes, Align Alignment, unsigned AddrSpace) const = 0; + virtual bool isLegalToVectorizeReduction(RecurKind RecKind, + bool Scalable) const = 0; virtual unsigned getLoadVectorFactor(unsigned VF, unsigned LoadSize, unsigned ChainSizeInBytes, VectorType *VecTy) const = 0; @@ -2170,6 +2176,10 @@ return Impl.isLegalToVectorizeStoreChain(ChainSizeInBytes, Alignment, AddrSpace); } + bool isLegalToVectorizeReduction(RecurKind RecKind, + bool Scalable) const override { + return Impl.isLegalToVectorizeReduction(RecKind, Scalable); + } unsigned getLoadVectorFactor(unsigned VF, unsigned LoadSize, unsigned ChainSizeInBytes, VectorType *VecTy) const override { Index: llvm/include/llvm/Analysis/TargetTransformInfoImpl.h =================================================================== --- llvm/include/llvm/Analysis/TargetTransformInfoImpl.h +++ llvm/include/llvm/Analysis/TargetTransformInfoImpl.h @@ -686,6 +686,10 @@ return true; } + bool isLegalToVectorizeReduction(RecurKind RecKind, bool Scalable) const { + return true; + } + unsigned getLoadVectorFactor(unsigned VF, unsigned LoadSize, unsigned ChainSizeInBytes, VectorType *VecTy) const { Index: llvm/lib/Analysis/TargetTransformInfo.cpp =================================================================== --- llvm/lib/Analysis/TargetTransformInfo.cpp +++ llvm/lib/Analysis/TargetTransformInfo.cpp @@ -1035,6 +1035,11 @@ AddrSpace); } +bool TargetTransformInfo::isLegalToVectorizeReduction(RecurKind RecKind, + bool Scalable) const { + return TTIImpl->isLegalToVectorizeReduction(RecKind, Scalable); +} + unsigned TargetTransformInfo::getLoadVectorFactor(unsigned VF, unsigned LoadSize, unsigned ChainSizeInBytes, Index: llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h =================================================================== --- llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h +++ llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h @@ -249,6 +249,8 @@ bool supportsScalableVectors() const { return ST->hasSVE(); } + bool isLegalToVectorizeReduction(RecurKind RecKind, bool Scalable) const; + bool useReductionIntrinsic(unsigned Opcode, Type *Ty, TTI::ReductionFlags Flags) const; Index: llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp +++ llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp @@ -1089,8 +1089,38 @@ return Considerable; } +bool AArch64TTIImpl::isLegalToVectorizeReduction(RecurKind RecKind, + bool Scalable) const { + if (Scalable) { + switch (RecKind) { + case RecurKind::Add: + case RecurKind::FAdd: + case RecurKind::And: + case RecurKind::Or: + case RecurKind::Xor: + case RecurKind::SMin: + case RecurKind::SMax: + case RecurKind::UMin: + case RecurKind::UMax: + case RecurKind::FMin: + case RecurKind::FMax: + return true; + default: + return false; + } + return false; + } + + return true; +} + bool AArch64TTIImpl::useReductionIntrinsic(unsigned Opcode, Type *Ty, TTI::ReductionFlags Flags) const { + if (isa(Ty)) { + assert(Opcode != Instruction::FMul && "Unexpected reduction opcode (FMul)"); + return true; + } + auto *VTy = cast(Ty); unsigned ScalarBits = Ty->getScalarSizeInBits(); switch (Opcode) { Index: llvm/lib/Transforms/Vectorize/LoopVectorize.cpp =================================================================== --- llvm/lib/Transforms/Vectorize/LoopVectorize.cpp +++ llvm/lib/Transforms/Vectorize/LoopVectorize.cpp @@ -1516,6 +1516,18 @@ (SI && isLegalMaskedScatter(Ty, Align)); } + /// Returns true if the target machine supports all of the reduction + /// variables found for the given VF + bool canVectorizeReductions(ElementCount VF) { + for (auto &Reduction : Legal->getReductionVars()) { + RecurrenceDescriptor RdxDesc = Reduction.second; + if (!TTI.isLegalToVectorizeReduction(RdxDesc.getRecurrenceKind(), + VF.isScalable())) + return false; + } + return true; + } + /// Returns true if \p I is an instruction that will be scalarized with /// predication. Such instructions include conditional stores and /// instructions that may divide by zero. @@ -4586,7 +4598,6 @@ RecurrenceDescriptor *RdxDesc, Value *StartV, unsigned UF, ElementCount VF) { - assert(!VF.isScalable() && "scalable vectors not yet supported."); PHINode *P = cast(PN); if (EnableVPlanNativePath) { // Currently we enter here in the VPlan-native path for non-induction @@ -5647,8 +5658,24 @@ // dependencies, check if it's legal. However, if a UserVF is specified and // there are no dependencies, then there's nothing to do. if (UserVF.isNonZero() && !IgnoreScalableUserVF && - Legal->isSafeForAnyVectorWidth()) + Legal->isSafeForAnyVectorWidth()) { + if (!canVectorizeReductions(UserVF)) { + LLVM_DEBUG(dbgs() << "LV: Scalable vectorization not supported for the " + "reduction operations found in this loop. " + "Using fixed-width vectorization instead.\n"); + ORE->emit([&]() { + return OptimizationRemarkAnalysis(DEBUG_TYPE, "ScalableVFUnfeasible", + TheLoop->getStartLoc(), + TheLoop->getHeader()) + << "Scalable vectorization not supported for the " + << "reduction operations found in this loop. " + << "Using fixed-width vectorization instead."; + }); + return computeFeasibleMaxVF( + ConstTripCount, ElementCount::getFixed(UserVF.getKnownMinValue())); + } return UserVF; + } MinBWs = computeMinimumValueSizes(TheLoop->getBlocks(), *DB, &TTI); unsigned SmallestType, WidestType; Index: llvm/test/Transforms/LoopVectorize/AArch64/scalable_reductions.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/LoopVectorize/AArch64/scalable_reductions.ll @@ -0,0 +1,397 @@ +; REQUIRES: asserts +; RUN: opt < %s -loop-vectorize -transform-warning -mtriple aarch64-unknown-linux-gnu -mattr=+sve -debug-only=loop-vectorize -S 2>%t | FileCheck %s -check-prefix=CHECK +; RUN: cat %t | FileCheck %s -check-prefix=CHECK-DEBUG +; RUN: cat %t | FileCheck %s -check-prefix=CHECK-WARN + +; Reduction can be vectorized + +; ADD + +define dso_local i32 @add(i32* nocapture %a, i32* nocapture readonly %b, i64 %n) { +; CHECK-LABEL: @add +; CHECK: vector.body: +; CHECK: %[[LOAD1:.*]] = load +; CHECK: %[[LOAD2:.*]] = load +; CHECK: %[[ADD1:.*]] = add %[[LOAD1]] +; CHECK: %[[ADD2:.*]] = add %[[LOAD2]] +; CHECK: middle.block: +; CHECK: %[[ADD:.*]] = add %[[ADD2]], %[[ADD1]] +; CHECK-NEXT: call i32 @llvm.vector.reduce.add.nxv8i32( %[[ADD]]) +entry: + %cmp6 = icmp sgt i64 %n, 0 + br i1 %cmp6, label %for.body, label %for.end + +for.body: ; preds = %entry, %for.body + %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ] + %sum.07 = phi i32 [ 2, %entry ], [ %add, %for.body ] + %arrayidx = getelementptr inbounds i32, i32* %a, i64 %iv + %0 = load i32, i32* %arrayidx, align 4 + %add = add nsw i32 %0, %sum.07 + %iv.next = add nuw nsw i64 %iv, 1 + %exitcond.not = icmp eq i64 %iv.next, %n + br i1 %exitcond.not, label %for.end, label %for.body, !llvm.loop !0 + +for.end: ; preds = %for.body, %entry + %sum.0.lcssa = phi i32 [ 2, %entry ], [ %add, %for.body ] + ret i32 %sum.0.lcssa +} + +; OR + +define dso_local i32 @or(i32* nocapture %a, i32* nocapture readonly %b, i64 %n) { +; CHECK-LABEL: @or +; CHECK: vector.body: +; CHECK: %[[LOAD1:.*]] = load +; CHECK: %[[LOAD2:.*]] = load +; CHECK: %[[OR1:.*]] = or %[[LOAD1]] +; CHECK: %[[OR2:.*]] = or %[[LOAD2]] +; CHECK: middle.block: +; CHECK: %[[OR:.*]] = or %[[OR2]], %[[OR1]] +; CHECK-NEXT: call i32 @llvm.vector.reduce.or.nxv8i32( %[[OR]]) +entry: + %cmp6 = icmp sgt i64 %n, 0 + br i1 %cmp6, label %for.body, label %for.end + +for.body: ; preds = %entry, %for.body + %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ] + %sum.07 = phi i32 [ 2, %entry ], [ %or, %for.body ] + %arrayidx = getelementptr inbounds i32, i32* %a, i64 %iv + %0 = load i32, i32* %arrayidx, align 4 + %or = or i32 %0, %sum.07 + %iv.next = add nuw nsw i64 %iv, 1 + %exitcond.not = icmp eq i64 %iv.next, %n + br i1 %exitcond.not, label %for.end, label %for.body, !llvm.loop !0 + +for.end: ; preds = %for.body, %entry + %sum.0.lcssa = phi i32 [ 2, %entry ], [ %or, %for.body ] + ret i32 %sum.0.lcssa +} + +; AND + +define dso_local i32 @and(i32* nocapture %a, i32* nocapture readonly %b, i64 %n) { +; CHECK-LABEL: @and +; CHECK: vector.body: +; CHECK: %[[LOAD1:.*]] = load +; CHECK: %[[LOAD2:.*]] = load +; CHECK: %[[AND1:.*]] = and %[[LOAD1]] +; CHECK: %[[AND2:.*]] = and %[[LOAD2]] +; CHECK: middle.block: +; CHECK: %[[ABD:.*]] = and %[[ADD2]], %[[AND1]] +; CHECK-NEXT: call i32 @llvm.vector.reduce.and.nxv8i32( %[[ADD]]) +entry: + %cmp6 = icmp sgt i64 %n, 0 + br i1 %cmp6, label %for.body, label %for.end + +for.body: ; preds = %entry, %for.body + %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ] + %sum.07 = phi i32 [ 2, %entry ], [ %and, %for.body ] + %arrayidx = getelementptr inbounds i32, i32* %a, i64 %iv + %0 = load i32, i32* %arrayidx, align 4 + %and = and i32 %0, %sum.07 + %iv.next = add nuw nsw i64 %iv, 1 + %exitcond.not = icmp eq i64 %iv.next, %n + br i1 %exitcond.not, label %for.end, label %for.body, !llvm.loop !0 + +for.end: ; preds = %for.body, %entry + %sum.0.lcssa = phi i32 [ 2, %entry ], [ %and, %for.body ] + ret i32 %sum.0.lcssa +} + +; XOR + +define dso_local i32 @xor(i32* nocapture %a, i32* nocapture readonly %b, i64 %n) { +; CHECK-LABEL: @xor +; CHECK: vector.body: +; CHECK: %[[LOAD1:.*]] = load +; CHECK: %[[LOAD2:.*]] = load +; CHECK: %[[XOR1:.*]] = xor %[[LOAD1]] +; CHECK: %[[XOR2:.*]] = xor %[[LOAD2]] +; CHECK: middle.block: +; CHECK: %[[XOR:.*]] = xor %[[XOR2]], %[[XOR1]] +; CHECK-NEXT: call i32 @llvm.vector.reduce.xor.nxv8i32( %[[XOR]]) +entry: + %cmp6 = icmp sgt i64 %n, 0 + br i1 %cmp6, label %for.body, label %for.end + +for.body: ; preds = %entry, %for.body + %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ] + %sum.07 = phi i32 [ 2, %entry ], [ %xor, %for.body ] + %arrayidx = getelementptr inbounds i32, i32* %a, i64 %iv + %0 = load i32, i32* %arrayidx, align 4 + %xor = xor i32 %0, %sum.07 + %iv.next = add nuw nsw i64 %iv, 1 + %exitcond.not = icmp eq i64 %iv.next, %n + br i1 %exitcond.not, label %for.end, label %for.body, !llvm.loop !0 + +for.end: ; preds = %for.body, %entry + %sum.0.lcssa = phi i32 [ 2, %entry ], [ %xor, %for.body ] + ret i32 %sum.0.lcssa +} + +; SMIN + +define dso_local i32 @smin(i32* nocapture %a, i32* nocapture readonly %b, i64 %n) { +; CHECK-LABEL: @smin +; CHECK: vector.body: +; CHECK: %[[LOAD1:.*]] = load +; CHECK: %[[LOAD2:.*]] = load +; CHECK: %[[ICMP1:.*]] = icmp slt %[[LOAD1]] +; CHECK: %[[ICMP2:.*]] = icmp slt %[[LOAD2]] +; CHECK: %[[SEL1:.*]] = select %[[ICMP1]], %[[LOAD1]] +; CHECK: %[[SEL2:.*]] = select %[[ICMP2]], %[[LOAD2]] +; CHECK: middle.block: +; CHECK: %[[ICMP:.*]] = icmp slt %[[SEL1]], %[[SEL2]] +; CHECK-NEXT: %[[SEL:.*]] = select %[[ICMP]], %[[SEL1]], %[[SEL2]] +; CHECK-NEXT: call i32 @llvm.vector.reduce.smin.nxv8i32( %[[SEL]]) +entry: + %cmp6 = icmp sgt i64 %n, 0 + br i1 %cmp6, label %for.body, label %for.end + +for.body: ; preds = %entry, %for.body + %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ] + %sum.010 = phi i32 [ 2, %entry ], [ %.sroa.speculated, %for.body ] + %arrayidx = getelementptr inbounds i32, i32* %a, i64 %iv + %0 = load i32, i32* %arrayidx, align 4 + %cmp.i = icmp slt i32 %0, %sum.010 + %.sroa.speculated = select i1 %cmp.i, i32 %0, i32 %sum.010 + %iv.next = add nuw nsw i64 %iv, 1 + %exitcond.not = icmp eq i64 %iv.next, %n + br i1 %exitcond.not, label %for.end, label %for.body, !llvm.loop !0 + +for.end: + %sum.0.lcssa = phi i32 [ 1, %entry ], [ %.sroa.speculated, %for.body ] + ret i32 %sum.0.lcssa +} + +; UMAX + +define dso_local i32 @umax(i32* nocapture %a, i32* nocapture readonly %b, i64 %n) { +; CHECK-LABEL: @umax +; CHECK: vector.body: +; CHECK: %[[LOAD1:.*]] = load +; CHECK: %[[LOAD2:.*]] = load +; CHECK: %[[ICMP1:.*]] = icmp ugt %[[LOAD1]] +; CHECK: %[[ICMP2:.*]] = icmp ugt %[[LOAD2]] +; CHECK: %[[SEL1:.*]] = select %[[ICMP1]], %[[LOAD1]] +; CHECK: %[[SEL2:.*]] = select %[[ICMP2]], %[[LOAD2]] +; CHECK: middle.block: +; CHECK: %[[ICMP:.*]] = icmp ugt %[[SEL1]], %[[SEL2]] +; CHECK-NEXT: %[[SEL:.*]] = select %[[ICMP]], %[[SEL1]], %[[SEL2]] +; CHECK-NEXT: call i32 @llvm.vector.reduce.umax.nxv8i32( %[[SEL]]) +entry: + %cmp6 = icmp sgt i64 %n, 0 + br i1 %cmp6, label %for.body, label %for.end + +for.body: ; preds = %entry, %for.body + %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ] + %sum.010 = phi i32 [ 2, %entry ], [ %.sroa.speculated, %for.body ] + %arrayidx = getelementptr inbounds i32, i32* %a, i64 %iv + %0 = load i32, i32* %arrayidx, align 4 + %cmp.i = icmp ugt i32 %0, %sum.010 + %.sroa.speculated = select i1 %cmp.i, i32 %0, i32 %sum.010 + %iv.next = add nuw nsw i64 %iv, 1 + %exitcond.not = icmp eq i64 %iv.next, %n + br i1 %exitcond.not, label %for.end, label %for.body, !llvm.loop !0 + +for.end: + %sum.0.lcssa = phi i32 [ 1, %entry ], [ %.sroa.speculated, %for.body ] + ret i32 %sum.0.lcssa +} + +; FADD (FAST) + +define dso_local float @fadd_fast(float* noalias nocapture readonly %a, i64 %n) { +; CHECK-LABEL: @fadd_fast +; CHECK: vector.body: +; CHECK: %[[LOAD1:.*]] = load +; CHECK: %[[LOAD2:.*]] = load +; CHECK: %[[ADD1:.*]] = fadd fast %[[LOAD1]] +; CHECK: %[[ADD2:.*]] = fadd fast %[[LOAD2]] +; CHECK: middle.block: +; CHECK: %[[ADD:.*]] = fadd fast %[[ADD2]], %[[ADD1]] +; CHECK-NEXT: call fast float @llvm.vector.reduce.fadd.nxv8f32(float -0.000000e+00, %[[ADD]]) +entry: + %cmp6 = icmp sgt i64 %n, 0 + br i1 %cmp6, label %for.body, label %for.end + +for.body: + %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ] + %sum.07 = phi float [ 0.000000e+00, %entry ], [ %add, %for.body ] + %arrayidx = getelementptr inbounds float, float* %a, i64 %iv + %0 = load float, float* %arrayidx, align 4 + %add = fadd fast float %0, %sum.07 + %iv.next = add nuw nsw i64 %iv, 1 + %exitcond.not = icmp eq i64 %iv.next, %n + br i1 %exitcond.not, label %for.end, label %for.body, !llvm.loop !0 + +for.end: + %sum.0.lcssa = phi float [ 0.000000e+00, %entry ], [ %add, %for.body ] + ret float %sum.0.lcssa +} + +; FMIN (FAST) + +define dso_local float @fmin_fast(float* noalias nocapture readonly %a, i64 %n) #0 { +; CHECK-LABEL: @fmin +; CHECK: vector.body: +; CHECK: %[[LOAD1:.*]] = load +; CHECK: %[[LOAD2:.*]] = load +; CHECK: %[[ICMP1:.*]] = fcmp fast olt %[[LOAD1]] +; CHECK: %[[ICMP2:.*]] = fcmp fast olt %[[LOAD2]] +; CHECK: %[[SEL1:.*]] = select %[[ICMP1]], %[[LOAD1]] +; CHECK: %[[SEL2:.*]] = select %[[ICMP2]], %[[LOAD2]] +; CHECK: middle.block: +; CHECK: %[[ICMP:.*]] = fcmp fast olt %[[SEL1]], %[[SEL2]] +; CHECK-NEXT: %[[SEL:.*]] = select fast %[[ICMP]], %[[SEL1]], %[[SEL2]] +; CHECK-NEXT: call float @llvm.vector.reduce.fmin.nxv8f32( %[[SEL]]) +entry: + %cmp6 = icmp sgt i64 %n, 0 + br i1 %cmp6, label %for.body, label %for.end + +for.body: + %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ] + %sum.07 = phi float [ 0.000000e+00, %entry ], [ %.sroa.speculated, %for.body ] + %arrayidx = getelementptr inbounds float, float* %a, i64 %iv + %0 = load float, float* %arrayidx, align 4 + %cmp.i = fcmp fast olt float %0, %sum.07 + %.sroa.speculated = select i1 %cmp.i, float %0, float %sum.07 + %iv.next = add nuw nsw i64 %iv, 1 + %exitcond.not = icmp eq i64 %iv.next, %n + br i1 %exitcond.not, label %for.end, label %for.body, !llvm.loop !0 + +for.end: + %sum.0.lcssa = phi float [ 0.000000e+00, %entry ], [ %.sroa.speculated, %for.body ] + ret float %sum.0.lcssa +} + +; FMAX (FAST) + +define dso_local float @fmax_fast(float* noalias nocapture readonly %a, i64 %n) #0 { +; CHECK-LABEL: @fmax_fast +; CHECK: vector.body: +; CHECK: %[[LOAD1:.*]] = load +; CHECK: %[[LOAD2:.*]] = load +; CHECK: %[[ICMP1:.*]] = fcmp fast ogt %[[LOAD1]] +; CHECK: %[[ICMP2:.*]] = fcmp fast ogt %[[LOAD2]] +; CHECK: %[[SEL1:.*]] = select %[[ICMP1]], %[[LOAD1]] +; CHECK: %[[SEL2:.*]] = select %[[ICMP2]], %[[LOAD2]] +; CHECK: middle.block: +; CHECK: %[[ICMP:.*]] = fcmp fast ogt %[[SEL1]], %[[SEL2]] +; CHECK-NEXT: %[[SEL:.*]] = select fast %[[ICMP]], %[[SEL1]], %[[SEL2]] +; CHECK-NEXT: call float @llvm.vector.reduce.fmax.nxv8f32( %[[SEL]]) +entry: + %cmp6 = icmp sgt i64 %n, 0 + br i1 %cmp6, label %for.body, label %for.end + +for.body: + %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ] + %sum.07 = phi float [ 0.000000e+00, %entry ], [ %.sroa.speculated, %for.body ] + %arrayidx = getelementptr inbounds float, float* %a, i64 %iv + %0 = load float, float* %arrayidx, align 4 + %cmp.i = fcmp fast ogt float %0, %sum.07 + %.sroa.speculated = select i1 %cmp.i, float %0, float %sum.07 + %iv.next = add nuw nsw i64 %iv, 1 + %exitcond.not = icmp eq i64 %iv.next, %n + br i1 %exitcond.not, label %for.end, label %for.body, !llvm.loop !0 + +for.end: + %sum.0.lcssa = phi float [ 0.000000e+00, %entry ], [ %.sroa.speculated, %for.body ] + ret float %sum.0.lcssa +} + +; Reduction cannot be vectorized + +; MUL + +; CHECK-DEBUG: LV: Scalable vectorization not supported for the reduction operations found in this loop. Using fixed-width vectorization instead. +define dso_local i32 @mul(i32* nocapture %a, i32* nocapture readonly %b, i64 %n) { +; CHECK-LABEL: @mul +; CHECK: vector.body: +; CHECK: %[[LOAD1:.*]] = load <8 x i32> +; CHECK: %[[LOAD2:.*]] = load <8 x i32> +; CHECK: %[[MUL1:.*]] = mul <8 x i32> %[[LOAD1]] +; CHECK: %[[MUL2:.*]] = mul <8 x i32> %[[LOAD2]] +; CHECK: middle.block: +; CHECK: %[[RDX1:.*]] = mul <8 x i32> %[[MUL2]], %[[MUL1]] +; CHECK: %[[SHUFFLE1:.*]] = shufflevector <8 x i32> %[[RDX1]], <8 x i32> poison, <8 x i32> +; CHECK: %[[RDX2:.*]] = mul <8 x i32> %[[RDX1]], %[[SHUFFLE1]] +; CHECK: %[[SHUFFLE2:.*]] = shufflevector <8 x i32> %[[RDX2]], <8 x i32> poison, <8 x i32> +; CHECK: %[[RDX3:.*]] = mul <8 x i32> %[[RDX2]], %[[SHUFFLE2]] +; CHECK: %[[SHUFFLE3:.*]] = shufflevector <8 x i32> %[[RDX3]], <8 x i32> poison, <8 x i32> +; CHECK: %[[RDX:.*]] = mul <8 x i32> %[[RDX3]], %[[SHUFFLE3]] +; CHECK: %[[EXTRACT:.*]] = extractelement <8 x i32> %[[RDX]], i32 0 +entry: + %cmp6 = icmp sgt i64 %n, 0 + br i1 %cmp6, label %for.body, label %for.end + +for.body: ; preds = %entry, %for.body + %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ] + %sum.07 = phi i32 [ 2, %entry ], [ %mul, %for.body ] + %arrayidx = getelementptr inbounds i32, i32* %a, i64 %iv + %0 = load i32, i32* %arrayidx, align 4 + %mul = mul nsw i32 %0, %sum.07 + %iv.next = add nuw nsw i64 %iv, 1 + %exitcond.not = icmp eq i64 %iv.next, %n + br i1 %exitcond.not, label %for.end, label %for.body, !llvm.loop !0 + +for.end: ; preds = %for.body, %entry + %sum.0.lcssa = phi i32 [ 2, %entry ], [ %mul, %for.body ] + ret i32 %sum.0.lcssa +} + +; FMIN + +; CHECK-WARN: warning: :0:0: loop not vectorized: the optimizer was unable to perform the requested transformation; the transformation might be disabled or specified as part of an unsupported transformation ordering +define dso_local float @fmin(float* noalias nocapture readonly %a, i64 %n) { +entry: + %cmp6 = icmp sgt i64 %n, 0 + br i1 %cmp6, label %for.body, label %for.end + +for.body: + %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ] + %sum.07 = phi float [ 0.000000e+00, %entry ], [ %.sroa.speculated, %for.body ] + %arrayidx = getelementptr inbounds float, float* %a, i64 %iv + %0 = load float, float* %arrayidx, align 4 + %cmp.i = fcmp olt float %0, %sum.07 + %.sroa.speculated = select i1 %cmp.i, float %0, float %sum.07 + %iv.next = add nuw nsw i64 %iv, 1 + %exitcond.not = icmp eq i64 %iv.next, %n + br i1 %exitcond.not, label %for.end, label %for.body, !llvm.loop !0 + +for.end: + %sum.0.lcssa = phi float [ 0.000000e+00, %entry ], [ %.sroa.speculated, %for.body ] + ret float %sum.0.lcssa +} + +; FMAX + +; CHECK-WARN: warning: :0:0: loop not vectorized: the optimizer was unable to perform the requested transformation; the transformation might be disabled or specified as part of an unsupported transformation ordering +define dso_local float @fmax(float* noalias nocapture readonly %a, i64 %n) { +entry: + %cmp6 = icmp sgt i64 %n, 0 + br i1 %cmp6, label %for.body, label %for.end + +for.body: + %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ] + %sum.07 = phi float [ 0.000000e+00, %entry ], [ %.sroa.speculated, %for.body ] + %arrayidx = getelementptr inbounds float, float* %a, i64 %iv + %0 = load float, float* %arrayidx, align 4 + %cmp.i = fcmp ogt float %0, %sum.07 + %.sroa.speculated = select i1 %cmp.i, float %0, float %sum.07 + %iv.next = add nuw nsw i64 %iv, 1 + %exitcond.not = icmp eq i64 %iv.next, %n + br i1 %exitcond.not, label %for.end, label %for.body, !llvm.loop !0 + +for.end: + %sum.0.lcssa = phi float [ 0.000000e+00, %entry ], [ %.sroa.speculated, %for.body ] + ret float %sum.0.lcssa +} + +attributes #0 = { "no-nans-fp-math"="true" } + +!0 = distinct !{!0, !1, !2, !3, !4} +!1 = !{!"llvm.loop.vectorize.width", i32 8} +!2 = !{!"llvm.loop.vectorize.scalable.enable", i1 true} +!3 = !{!"llvm.loop.interleave.count", i32 2} +!4 = !{!"llvm.loop.vectorize.enable", i1 true}