Instead move them to the instcombine that happens in AArch64TargetTransformInfo.
Differential Revision: https://reviews.llvm.org/D106144
: None;
}
+static Optional<Instruction *> instCombineSVEPTest(InstCombiner &IC,
+ IntrinsicInst &II) {
+ IntrinsicInst *Op1 = dyn_cast<IntrinsicInst>(II.getArgOperand(0));
+ IntrinsicInst *Op2 = dyn_cast<IntrinsicInst>(II.getArgOperand(1));
+
+ if (Op1 && Op2 &&
+ Op1->getIntrinsicID() == Intrinsic::aarch64_sve_convert_to_svbool &&
+ Op2->getIntrinsicID() == Intrinsic::aarch64_sve_convert_to_svbool &&
+ Op1->getArgOperand(0)->getType() == Op2->getArgOperand(0)->getType()) {
+
+ IRBuilder<> Builder(II.getContext());
+ Builder.SetInsertPoint(&II);
+
+ Value *Ops[] = {Op1->getArgOperand(0), Op2->getArgOperand(0)};
+ Type *Tys[] = {Op1->getArgOperand(0)->getType()};
+
+ auto *PTest = Builder.CreateIntrinsic(II.getIntrinsicID(), Tys, Ops);
+
+ PTest->takeName(&II);
+ return IC.replaceInstUsesWith(II, PTest);
+ }
+
+ return None;
+}
+
+static Optional<Instruction *> instCombineSVEVectorMul(InstCombiner &IC,
+ IntrinsicInst &II) {
+ auto *OpPredicate = II.getOperand(0);
+ auto *OpMultiplicand = II.getOperand(1);
+ auto *OpMultiplier = II.getOperand(2);
+
+ IRBuilder<> Builder(II.getContext());
+ Builder.SetInsertPoint(&II);
+
+ // Return true if a given instruction is an aarch64_sve_dup_x intrinsic call
+ // with a unit splat value, false otherwise.
+ auto IsUnitDupX = [](auto *I) {
+ auto *IntrI = dyn_cast<IntrinsicInst>(I);
+ if (!IntrI || IntrI->getIntrinsicID() != Intrinsic::aarch64_sve_dup_x)
+ return false;
+
+ auto *SplatValue = IntrI->getOperand(0);
+ return match(SplatValue, m_FPOne()) || match(SplatValue, m_One());
+ };
+
+ // Return true if a given instruction is an aarch64_sve_dup intrinsic call
+ // with a unit splat value, false otherwise.
+ auto IsUnitDup = [](auto *I) {
+ auto *IntrI = dyn_cast<IntrinsicInst>(I);
+ if (!IntrI || IntrI->getIntrinsicID() != Intrinsic::aarch64_sve_dup)
+ return false;
+
+ auto *SplatValue = IntrI->getOperand(2);
+ return match(SplatValue, m_FPOne()) || match(SplatValue, m_One());
+ };
+
+ // The OpMultiplier variable should always point to the dup (if any), so
+ // swap if necessary.
+ if (IsUnitDup(OpMultiplicand) || IsUnitDupX(OpMultiplicand))
+ std::swap(OpMultiplier, OpMultiplicand);
+
+ if (IsUnitDupX(OpMultiplier)) {
+ // [f]mul pg (dupx 1) %n => %n
+ OpMultiplicand->takeName(&II);
+ return IC.replaceInstUsesWith(II, OpMultiplicand);
+ } else if (IsUnitDup(OpMultiplier)) {
+ // [f]mul pg (dup pg 1) %n => %n
+ auto *DupInst = cast<IntrinsicInst>(OpMultiplier);
+ auto *DupPg = DupInst->getOperand(1);
+ // TODO: this is naive. The optimization is still valid if DupPg
+ // 'encompasses' OpPredicate, not only if they're the same predicate.
+ if (OpPredicate == DupPg) {
+ OpMultiplicand->takeName(&II);
+ return IC.replaceInstUsesWith(II, OpMultiplicand);
+ }
+ }
+
+ return None;
+}
+
+static Optional<Instruction *> instCombineSVETBL(InstCombiner &IC,
+ IntrinsicInst &II) {
+ auto *OpVal = II.getOperand(0);
+ auto *OpIndices = II.getOperand(1);
+ VectorType *VTy = cast<VectorType>(II.getType());
+
+ // Check whether OpIndices is an aarch64_sve_dup_x intrinsic call with
+ // constant splat value < minimal element count of result.
+ auto *DupXIntrI = dyn_cast<IntrinsicInst>(OpIndices);
+ if (!DupXIntrI || DupXIntrI->getIntrinsicID() != Intrinsic::aarch64_sve_dup_x)
+ return None;
+
+ auto *SplatValue = dyn_cast<ConstantInt>(DupXIntrI->getOperand(0));
+ if (!SplatValue ||
+ SplatValue->getValue().uge(VTy->getElementCount().getKnownMinValue()))
+ return None;
+
+ // Convert sve_tbl(OpVal sve_dup_x(SplatValue)) to
+ // splat_vector(extractelement(OpVal, SplatValue)) for further optimization.
+ IRBuilder<> Builder(II.getContext());
+ Builder.SetInsertPoint(&II);
+ auto *Extract = Builder.CreateExtractElement(OpVal, SplatValue);
+ auto *VectorSplat =
+ Builder.CreateVectorSplat(VTy->getElementCount(), Extract);
+
+ VectorSplat->takeName(&II);
+ return IC.replaceInstUsesWith(II, VectorSplat);
+}
+
Optional<Instruction *>
AArch64TTIImpl::instCombineIntrinsic(InstCombiner &IC,
IntrinsicInst &II) const {
return instCombineSVECntElts(IC, II, 8);
case Intrinsic::aarch64_sve_cntb:
return instCombineSVECntElts(IC, II, 16);
+ case Intrinsic::aarch64_sve_ptest_any:
+ case Intrinsic::aarch64_sve_ptest_first:
+ case Intrinsic::aarch64_sve_ptest_last:
+ return instCombineSVEPTest(IC, II);
+ case Intrinsic::aarch64_sve_mul:
+ case Intrinsic::aarch64_sve_fmul:
+ return instCombineSVEVectorMul(IC, II);
+ case Intrinsic::aarch64_sve_tbl:
+ return instCombineSVETBL(IC, II);
}
return None;
SmallSetVector<IntrinsicInst *, 4> &PTrues);
bool optimizePTrueIntrinsicCalls(SmallSetVector<Function *, 4> &Functions);
- /// Operates at the instruction-scope. I.e., optimizations are applied local
- /// to individual instructions.
- static bool optimizeIntrinsic(Instruction *I);
- bool optimizeIntrinsicCalls(SmallSetVector<Function *, 4> &Functions);
-
/// Operates at the function-scope. I.e., optimizations are applied local to
/// the functions themselves.
bool optimizeFunctions(SmallSetVector<Function *, 4> &Functions);
-
- static bool optimizePTest(IntrinsicInst *I);
- static bool optimizeVectorMul(IntrinsicInst *I);
- static bool optimizeTBL(IntrinsicInst *I);
};
} // end anonymous namespace
return Changed;
}
-bool SVEIntrinsicOpts::optimizePTest(IntrinsicInst *I) {
- IntrinsicInst *Op1 = dyn_cast<IntrinsicInst>(I->getArgOperand(0));
- IntrinsicInst *Op2 = dyn_cast<IntrinsicInst>(I->getArgOperand(1));
-
- if (Op1 && Op2 &&
- Op1->getIntrinsicID() == Intrinsic::aarch64_sve_convert_to_svbool &&
- Op2->getIntrinsicID() == Intrinsic::aarch64_sve_convert_to_svbool &&
- Op1->getArgOperand(0)->getType() == Op2->getArgOperand(0)->getType()) {
-
- Value *Ops[] = {Op1->getArgOperand(0), Op2->getArgOperand(0)};
- Type *Tys[] = {Op1->getArgOperand(0)->getType()};
- Module *M = I->getParent()->getParent()->getParent();
-
- auto Fn = Intrinsic::getDeclaration(M, I->getIntrinsicID(), Tys);
- auto CI = CallInst::Create(Fn, Ops, I->getName(), I);
-
- I->replaceAllUsesWith(CI);
- I->eraseFromParent();
- if (Op1->use_empty())
- Op1->eraseFromParent();
- if (Op1 != Op2 && Op2->use_empty())
- Op2->eraseFromParent();
-
- return true;
- }
-
- return false;
-}
-
-bool SVEIntrinsicOpts::optimizeVectorMul(IntrinsicInst *I) {
- assert((I->getIntrinsicID() == Intrinsic::aarch64_sve_mul ||
- I->getIntrinsicID() == Intrinsic::aarch64_sve_fmul) &&
- "Unexpected opcode");
-
- auto *OpPredicate = I->getOperand(0);
- auto *OpMultiplicand = I->getOperand(1);
- auto *OpMultiplier = I->getOperand(2);
-
- // Return true if a given instruction is an aarch64_sve_dup_x intrinsic call
- // with a unit splat value, false otherwise.
- auto IsUnitDupX = [](auto *I) {
- auto *IntrI = dyn_cast<IntrinsicInst>(I);
- if (!IntrI || IntrI->getIntrinsicID() != Intrinsic::aarch64_sve_dup_x)
- return false;
-
- auto *SplatValue = IntrI->getOperand(0);
- return match(SplatValue, m_FPOne()) || match(SplatValue, m_One());
- };
-
- // Return true if a given instruction is an aarch64_sve_dup intrinsic call
- // with a unit splat value, false otherwise.
- auto IsUnitDup = [](auto *I) {
- auto *IntrI = dyn_cast<IntrinsicInst>(I);
- if (!IntrI || IntrI->getIntrinsicID() != Intrinsic::aarch64_sve_dup)
- return false;
-
- auto *SplatValue = IntrI->getOperand(2);
- return match(SplatValue, m_FPOne()) || match(SplatValue, m_One());
- };
-
- bool Changed = true;
-
- // The OpMultiplier variable should always point to the dup (if any), so
- // swap if necessary.
- if (IsUnitDup(OpMultiplicand) || IsUnitDupX(OpMultiplicand))
- std::swap(OpMultiplier, OpMultiplicand);
-
- if (IsUnitDupX(OpMultiplier)) {
- // [f]mul pg (dupx 1) %n => %n
- I->replaceAllUsesWith(OpMultiplicand);
- I->eraseFromParent();
- Changed = true;
- } else if (IsUnitDup(OpMultiplier)) {
- // [f]mul pg (dup pg 1) %n => %n
- auto *DupInst = cast<IntrinsicInst>(OpMultiplier);
- auto *DupPg = DupInst->getOperand(1);
- // TODO: this is naive. The optimization is still valid if DupPg
- // 'encompasses' OpPredicate, not only if they're the same predicate.
- if (OpPredicate == DupPg) {
- I->replaceAllUsesWith(OpMultiplicand);
- I->eraseFromParent();
- Changed = true;
- }
- }
-
- // If an instruction was optimized out then it is possible that some dangling
- // instructions are left.
- if (Changed) {
- auto *OpPredicateInst = dyn_cast<Instruction>(OpPredicate);
- auto *OpMultiplierInst = dyn_cast<Instruction>(OpMultiplier);
- if (OpMultiplierInst && OpMultiplierInst->use_empty())
- OpMultiplierInst->eraseFromParent();
- if (OpPredicateInst && OpPredicateInst->use_empty())
- OpPredicateInst->eraseFromParent();
- }
-
- return Changed;
-}
-
-bool SVEIntrinsicOpts::optimizeTBL(IntrinsicInst *I) {
- assert(I->getIntrinsicID() == Intrinsic::aarch64_sve_tbl &&
- "Unexpected opcode");
-
- auto *OpVal = I->getOperand(0);
- auto *OpIndices = I->getOperand(1);
- VectorType *VTy = cast<VectorType>(I->getType());
-
- // Check whether OpIndices is an aarch64_sve_dup_x intrinsic call with
- // constant splat value < minimal element count of result.
- auto *DupXIntrI = dyn_cast<IntrinsicInst>(OpIndices);
- if (!DupXIntrI || DupXIntrI->getIntrinsicID() != Intrinsic::aarch64_sve_dup_x)
- return false;
-
- auto *SplatValue = dyn_cast<ConstantInt>(DupXIntrI->getOperand(0));
- if (!SplatValue ||
- SplatValue->getValue().uge(VTy->getElementCount().getKnownMinValue()))
- return false;
-
- // Convert sve_tbl(OpVal sve_dup_x(SplatValue)) to
- // splat_vector(extractelement(OpVal, SplatValue)) for further optimization.
- LLVMContext &Ctx = I->getContext();
- IRBuilder<> Builder(Ctx);
- Builder.SetInsertPoint(I);
- auto *Extract = Builder.CreateExtractElement(OpVal, SplatValue);
- auto *VectorSplat =
- Builder.CreateVectorSplat(VTy->getElementCount(), Extract);
-
- I->replaceAllUsesWith(VectorSplat);
- I->eraseFromParent();
- if (DupXIntrI->use_empty())
- DupXIntrI->eraseFromParent();
- return true;
-}
-
-bool SVEIntrinsicOpts::optimizeIntrinsic(Instruction *I) {
- IntrinsicInst *IntrI = dyn_cast<IntrinsicInst>(I);
- if (!IntrI)
- return false;
-
- switch (IntrI->getIntrinsicID()) {
- case Intrinsic::aarch64_sve_fmul:
- case Intrinsic::aarch64_sve_mul:
- return optimizeVectorMul(IntrI);
- case Intrinsic::aarch64_sve_ptest_any:
- case Intrinsic::aarch64_sve_ptest_first:
- case Intrinsic::aarch64_sve_ptest_last:
- return optimizePTest(IntrI);
- case Intrinsic::aarch64_sve_tbl:
- return optimizeTBL(IntrI);
- default:
- return false;
- }
-
- return true;
-}
-
-bool SVEIntrinsicOpts::optimizeIntrinsicCalls(
- SmallSetVector<Function *, 4> &Functions) {
- bool Changed = false;
- for (auto *F : Functions) {
- DominatorTree *DT = &getAnalysis<DominatorTreeWrapperPass>(*F).getDomTree();
-
- // Traverse the DT with an rpo walk so we see defs before uses, allowing
- // simplification to be done incrementally.
- BasicBlock *Root = DT->getRoot();
- ReversePostOrderTraversal<BasicBlock *> RPOT(Root);
- for (auto *BB : RPOT)
- for (Instruction &I : make_early_inc_range(*BB))
- Changed |= optimizeIntrinsic(&I);
- }
- return Changed;
-}
-
bool SVEIntrinsicOpts::optimizeFunctions(
SmallSetVector<Function *, 4> &Functions) {
bool Changed = false;
Changed |= optimizePTrueIntrinsicCalls(Functions);
- Changed |= optimizeIntrinsicCalls(Functions);
return Changed;
}
continue;
switch (F.getIntrinsicID()) {
- case Intrinsic::aarch64_sve_ptest_any:
- case Intrinsic::aarch64_sve_ptest_first:
- case Intrinsic::aarch64_sve_ptest_last:
case Intrinsic::aarch64_sve_ptrue:
- case Intrinsic::aarch64_sve_mul:
- case Intrinsic::aarch64_sve_fmul:
- case Intrinsic::aarch64_sve_tbl:
for (User *U : F.users())
Functions.insert(cast<Instruction>(U)->getFunction());
break;
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S -aarch64-sve-intrinsic-opts < %s | FileCheck %s
+; RUN: opt -S -instcombine < %s | FileCheck %s
+
+target triple = "aarch64-unknown-linux-gnu"
; Idempotent fmuls -- should compile to just a ret.
-define <vscale x 8 x half> @idempotent_fmul_f16(<vscale x 8 x i1> %pg, <vscale x 8 x half> %a) {
+define <vscale x 8 x half> @idempotent_fmul_f16(<vscale x 8 x i1> %pg, <vscale x 8 x half> %a) #0 {
; CHECK-LABEL: @idempotent_fmul_f16(
; CHECK-NEXT: ret <vscale x 8 x half> [[A:%.*]]
;
ret <vscale x 8 x half> %2
}
-define <vscale x 4 x float> @idempotent_fmul_f32(<vscale x 4 x i1> %pg, <vscale x 4 x float> %a) {
+define <vscale x 4 x float> @idempotent_fmul_f32(<vscale x 4 x i1> %pg, <vscale x 4 x float> %a) #0 {
; CHECK-LABEL: @idempotent_fmul_f32(
; CHECK-NEXT: ret <vscale x 4 x float> [[A:%.*]]
;
ret <vscale x 4 x float> %2
}
-define <vscale x 2 x double> @idempotent_fmul_f64(<vscale x 2 x i1> %pg, <vscale x 2 x double> %a) {
+define <vscale x 2 x double> @idempotent_fmul_f64(<vscale x 2 x i1> %pg, <vscale x 2 x double> %a) #0 {
; CHECK-LABEL: @idempotent_fmul_f64(
; CHECK-NEXT: ret <vscale x 2 x double> [[A:%.*]]
;
ret <vscale x 2 x double> %2
}
-define <vscale x 2 x double> @idempotent_fmul_different_argument_order(<vscale x 2 x i1> %pg, <vscale x 2 x double> %a) {
+define <vscale x 2 x double> @idempotent_fmul_different_argument_order(<vscale x 2 x i1> %pg, <vscale x 2 x double> %a) #0 {
; CHECK-LABEL: @idempotent_fmul_different_argument_order(
; CHECK-NEXT: ret <vscale x 2 x double> [[A:%.*]]
;
ret <vscale x 2 x double> %2
}
-define <vscale x 8 x half> @idempotent_fmul_with_predicated_dup(<vscale x 8 x i1> %pg, <vscale x 8 x half> %a) {
+define <vscale x 8 x half> @idempotent_fmul_with_predicated_dup(<vscale x 8 x i1> %pg, <vscale x 8 x half> %a) #0 {
; CHECK-LABEL: @idempotent_fmul_with_predicated_dup(
; CHECK-NEXT: ret <vscale x 8 x half> [[A:%.*]]
;
ret <vscale x 8 x half> %2
}
-define <vscale x 8 x half> @idempotent_fmul_two_dups(<vscale x 8 x i1> %pg, <vscale x 8 x half> %a) {
+define <vscale x 8 x half> @idempotent_fmul_two_dups(<vscale x 8 x i1> %pg, <vscale x 8 x half> %a) #0 {
; Edge case -- make sure that the case where we're fmultiplying two dups
; together is sane.
; CHECK-LABEL: @idempotent_fmul_two_dups(
}
; Non-idempotent fmuls -- we don't expect these to be optimised out.
-define <vscale x 8 x half> @non_idempotent_fmul_f16(<vscale x 8 x i1> %pg, <vscale x 8 x half> %a) {
+define <vscale x 8 x half> @non_idempotent_fmul_f16(<vscale x 8 x i1> %pg, <vscale x 8 x half> %a) #0 {
; CHECK-LABEL: @non_idempotent_fmul_f16(
; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 8 x half> @llvm.aarch64.sve.dup.x.nxv8f16(half 0xH4000)
; CHECK-NEXT: [[TMP2:%.*]] = call <vscale x 8 x half> @llvm.aarch64.sve.fmul.nxv8f16(<vscale x 8 x i1> [[PG:%.*]], <vscale x 8 x half> [[A:%.*]], <vscale x 8 x half> [[TMP1]])
ret <vscale x 8 x half> %2
}
-define <vscale x 4 x float> @non_idempotent_fmul_f32(<vscale x 4 x i1> %pg, <vscale x 4 x float> %a) {
+define <vscale x 4 x float> @non_idempotent_fmul_f32(<vscale x 4 x i1> %pg, <vscale x 4 x float> %a) #0 {
; CHECK-LABEL: @non_idempotent_fmul_f32(
; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 4 x float> @llvm.aarch64.sve.dup.x.nxv4f32(float 2.000000e+00)
; CHECK-NEXT: [[TMP2:%.*]] = call <vscale x 4 x float> @llvm.aarch64.sve.fmul.nxv4f32(<vscale x 4 x i1> [[PG:%.*]], <vscale x 4 x float> [[A:%.*]], <vscale x 4 x float> [[TMP1]])
ret <vscale x 4 x float> %2
}
-define <vscale x 2 x double> @non_idempotent_fmul_f64(<vscale x 2 x i1> %pg, <vscale x 2 x double> %a) {
+define <vscale x 2 x double> @non_idempotent_fmul_f64(<vscale x 2 x i1> %pg, <vscale x 2 x double> %a) #0 {
; CHECK-LABEL: @non_idempotent_fmul_f64(
; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 2 x double> @llvm.aarch64.sve.dup.x.nxv2f64(double 2.000000e+00)
; CHECK-NEXT: [[TMP2:%.*]] = call <vscale x 2 x double> @llvm.aarch64.sve.fmul.nxv2f64(<vscale x 2 x i1> [[PG:%.*]], <vscale x 2 x double> [[A:%.*]], <vscale x 2 x double> [[TMP1]])
ret <vscale x 2 x double> %2
}
-define <vscale x 2 x double> @non_idempotent_fmul_with_predicated_dup(<vscale x 2 x i1> %pg1, <vscale x 2 x i1> %pg2, <vscale x 2 x double> %a) {
+define <vscale x 2 x double> @non_idempotent_fmul_with_predicated_dup(<vscale x 2 x i1> %pg1, <vscale x 2 x i1> %pg2, <vscale x 2 x double> %a) #0 {
; Different predicates
; CHECK-LABEL: @non_idempotent_fmul_with_predicated_dup(
; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 2 x double> @llvm.aarch64.sve.dup.nxv2f64(<vscale x 2 x double> undef, <vscale x 2 x i1> [[PG1:%.*]], double 1.000000e+00)
declare <vscale x 8 x half> @llvm.aarch64.sve.fmul.nxv8f16(<vscale x 8 x i1>, <vscale x 8 x half>, <vscale x 8 x half>)
declare <vscale x 4 x float> @llvm.aarch64.sve.fmul.nxv4f32(<vscale x 4 x i1>, <vscale x 4 x float>, <vscale x 4 x float>)
declare <vscale x 2 x double> @llvm.aarch64.sve.fmul.nxv2f64(<vscale x 2 x i1>, <vscale x 2 x double>, <vscale x 2 x double>)
+
+attributes #0 = { "target-features"="+sve" }
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S -aarch64-sve-intrinsic-opts < %s | FileCheck %s
+; RUN: opt -S -instcombine < %s | FileCheck %s
+
+target triple = "aarch64-unknown-linux-gnu"
; Idempotent muls -- should compile to just a ret.
-define <vscale x 8 x i16> @idempotent_mul_i16(<vscale x 8 x i1> %pg, <vscale x 8 x i16> %a) {
+define <vscale x 8 x i16> @idempotent_mul_i16(<vscale x 8 x i1> %pg, <vscale x 8 x i16> %a) #0 {
; CHECK-LABEL: @idempotent_mul_i16(
; CHECK-NEXT: ret <vscale x 8 x i16> [[A:%.*]]
;
ret <vscale x 8 x i16> %2
}
-define <vscale x 4 x i32> @idempotent_mul_i32(<vscale x 4 x i1> %pg, <vscale x 4 x i32> %a) {
+define <vscale x 4 x i32> @idempotent_mul_i32(<vscale x 4 x i1> %pg, <vscale x 4 x i32> %a) #0 {
; CHECK-LABEL: @idempotent_mul_i32(
; CHECK-NEXT: ret <vscale x 4 x i32> [[A:%.*]]
;
ret <vscale x 4 x i32> %2
}
-define <vscale x 2 x i64> @idempotent_mul_i64(<vscale x 2 x i1> %pg, <vscale x 2 x i64> %a) {
+define <vscale x 2 x i64> @idempotent_mul_i64(<vscale x 2 x i1> %pg, <vscale x 2 x i64> %a) #0 {
; CHECK-LABEL: @idempotent_mul_i64(
; CHECK-NEXT: ret <vscale x 2 x i64> [[A:%.*]]
;
ret <vscale x 2 x i64> %2
}
-define <vscale x 2 x i64> @idempotent_mul_different_argument_order(<vscale x 2 x i1> %pg, <vscale x 2 x i64> %a) {
+define <vscale x 2 x i64> @idempotent_mul_different_argument_order(<vscale x 2 x i1> %pg, <vscale x 2 x i64> %a) #0 {
; CHECK-LABEL: @idempotent_mul_different_argument_order(
; CHECK-NEXT: ret <vscale x 2 x i64> [[A:%.*]]
;
ret <vscale x 2 x i64> %2
}
-define <vscale x 8 x i16> @idempotent_mul_with_predicated_dup(<vscale x 8 x i1> %pg, <vscale x 8 x i16> %a) {
+define <vscale x 8 x i16> @idempotent_mul_with_predicated_dup(<vscale x 8 x i1> %pg, <vscale x 8 x i16> %a) #0 {
; CHECK-LABEL: @idempotent_mul_with_predicated_dup(
; CHECK-NEXT: ret <vscale x 8 x i16> [[A:%.*]]
;
ret <vscale x 8 x i16> %2
}
-define <vscale x 8 x i16> @idempotent_mul_two_dups(<vscale x 8 x i1> %pg, <vscale x 8 x i16> %a) {
+define <vscale x 8 x i16> @idempotent_mul_two_dups(<vscale x 8 x i1> %pg, <vscale x 8 x i16> %a) #0 {
; Edge case -- make sure that the case where we're multiplying two dups
; together is sane.
; CHECK-LABEL: @idempotent_mul_two_dups(
}
; Non-idempotent muls -- we don't expect these to be optimised out.
-define <vscale x 8 x i16> @non_idempotent_mul_i16(<vscale x 8 x i1> %pg, <vscale x 8 x i16> %a) {
+define <vscale x 8 x i16> @non_idempotent_mul_i16(<vscale x 8 x i1> %pg, <vscale x 8 x i16> %a) #0 {
; CHECK-LABEL: @non_idempotent_mul_i16(
; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.dup.x.nxv8i16(i16 2)
; CHECK-NEXT: [[TMP2:%.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.mul.nxv8i16(<vscale x 8 x i1> [[PG:%.*]], <vscale x 8 x i16> [[A:%.*]], <vscale x 8 x i16> [[TMP1]])
ret <vscale x 8 x i16> %2
}
-define <vscale x 4 x i32> @non_idempotent_mul_i32(<vscale x 4 x i1> %pg, <vscale x 4 x i32> %a) {
+define <vscale x 4 x i32> @non_idempotent_mul_i32(<vscale x 4 x i1> %pg, <vscale x 4 x i32> %a) #0 {
; CHECK-LABEL: @non_idempotent_mul_i32(
; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 4 x i32> @llvm.aarch64.sve.dup.x.nxv4i32(i32 2)
; CHECK-NEXT: [[TMP2:%.*]] = call <vscale x 4 x i32> @llvm.aarch64.sve.mul.nxv4i32(<vscale x 4 x i1> [[PG:%.*]], <vscale x 4 x i32> [[A:%.*]], <vscale x 4 x i32> [[TMP1]])
ret <vscale x 4 x i32> %2
}
-define <vscale x 2 x i64> @non_idempotent_mul_i64(<vscale x 2 x i1> %pg, <vscale x 2 x i64> %a) {
+define <vscale x 2 x i64> @non_idempotent_mul_i64(<vscale x 2 x i1> %pg, <vscale x 2 x i64> %a) #0 {
; CHECK-LABEL: @non_idempotent_mul_i64(
; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 2 x i64> @llvm.aarch64.sve.dup.x.nxv2i64(i64 2)
; CHECK-NEXT: [[TMP2:%.*]] = call <vscale x 2 x i64> @llvm.aarch64.sve.mul.nxv2i64(<vscale x 2 x i1> [[PG:%.*]], <vscale x 2 x i64> [[A:%.*]], <vscale x 2 x i64> [[TMP1]])
ret <vscale x 2 x i64> %2
}
-define <vscale x 2 x i64> @non_idempotent_mul_with_predicated_dup(<vscale x 2 x i1> %pg1, <vscale x 2 x i1> %pg2, <vscale x 2 x i64> %a) {
+define <vscale x 2 x i64> @non_idempotent_mul_with_predicated_dup(<vscale x 2 x i1> %pg1, <vscale x 2 x i1> %pg2, <vscale x 2 x i64> %a) #0 {
; Different predicates
; CHECK-LABEL: @non_idempotent_mul_with_predicated_dup(
; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 2 x i64> @llvm.aarch64.sve.dup.nxv2i64(<vscale x 2 x i64> undef, <vscale x 2 x i1> [[PG1:%.*]], i64 1)
declare <vscale x 8 x i16> @llvm.aarch64.sve.mul.nxv8i16(<vscale x 8 x i1>, <vscale x 8 x i16>, <vscale x 8 x i16>)
declare <vscale x 4 x i32> @llvm.aarch64.sve.mul.nxv4i32(<vscale x 4 x i1>, <vscale x 4 x i32>, <vscale x 4 x i32>)
declare <vscale x 2 x i64> @llvm.aarch64.sve.mul.nxv2i64(<vscale x 2 x i1>, <vscale x 2 x i64>, <vscale x 2 x i64>)
+
+attributes #0 = { "target-features"="+sve" }
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S -aarch64-sve-intrinsic-opts -mtriple=aarch64-linux-gnu -mattr=+sve < %s | FileCheck %s
+; RUN: opt -S -instcombine < %s | FileCheck %s
+
+target triple = "aarch64-unknown-linux-gnu"
; op2 = tbl(op1 dup_x(idx)) -> op2 = vector_splat(extractelement(op1, idx))
-define <vscale x 16 x i8> @dup_ext_i8(<vscale x 16 x i8> %data) {
+define <vscale x 16 x i8> @dup_ext_i8(<vscale x 16 x i8> %data) #0 {
; CHECK-LABEL: @dup_ext_i8(
; CHECK-NEXT: [[TMP1:%.*]] = extractelement <vscale x 16 x i8> [[DATA:%.*]], i8 1
; CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <vscale x 16 x i8> poison, i8 [[TMP1]], i32 0
ret <vscale x 16 x i8> %out
}
-define <vscale x 8 x i16> @dup_ext_i16(<vscale x 8 x i16> %data) {
+define <vscale x 8 x i16> @dup_ext_i16(<vscale x 8 x i16> %data) #0 {
; CHECK-LABEL: @dup_ext_i16(
; CHECK-NEXT: [[TMP1:%.*]] = extractelement <vscale x 8 x i16> [[DATA:%.*]], i16 1
; CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <vscale x 8 x i16> poison, i16 [[TMP1]], i32 0
ret <vscale x 8 x i16> %out
}
-define <vscale x 4 x i32> @dup_ext_i32(<vscale x 4 x i32> %data) {
+define <vscale x 4 x i32> @dup_ext_i32(<vscale x 4 x i32> %data) #0 {
; CHECK-LABEL: @dup_ext_i32(
; CHECK-NEXT: [[TMP1:%.*]] = extractelement <vscale x 4 x i32> [[DATA:%.*]], i32 1
; CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <vscale x 4 x i32> poison, i32 [[TMP1]], i32 0
ret <vscale x 4 x i32> %out
}
-define <vscale x 2 x i64> @dup_ext_i64(<vscale x 2 x i64> %data) {
+define <vscale x 2 x i64> @dup_ext_i64(<vscale x 2 x i64> %data) #0 {
; CHECK-LABEL: @dup_ext_i64(
; CHECK-NEXT: [[TMP1:%.*]] = extractelement <vscale x 2 x i64> [[DATA:%.*]], i64 1
; CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <vscale x 2 x i64> poison, i64 [[TMP1]], i32 0
ret <vscale x 2 x i64> %out
}
-define <vscale x 8 x half> @dup_ext_f16(<vscale x 8 x half> %data) {
+define <vscale x 8 x half> @dup_ext_f16(<vscale x 8 x half> %data) #0 {
; CHECK-LABEL: @dup_ext_f16(
; CHECK-NEXT: [[TMP1:%.*]] = extractelement <vscale x 8 x half> [[DATA:%.*]], i16 1
; CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <vscale x 8 x half> poison, half [[TMP1]], i32 0
ret <vscale x 8 x half> %out
}
-define <vscale x 4 x float> @dup_ext_f32(<vscale x 4 x float> %data) {
+define <vscale x 4 x float> @dup_ext_f32(<vscale x 4 x float> %data) #0 {
; CHECK-LABEL: @dup_ext_f32(
; CHECK-NEXT: [[TMP1:%.*]] = extractelement <vscale x 4 x float> [[DATA:%.*]], i32 1
; CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <vscale x 4 x float> poison, float [[TMP1]], i32 0
ret <vscale x 4 x float> %out
}
-define <vscale x 2 x double> @dup_ext_f64(<vscale x 2 x double> %data) {
+define <vscale x 2 x double> @dup_ext_f64(<vscale x 2 x double> %data) #0 {
; CHECK-LABEL: @dup_ext_f64(
; CHECK-NEXT: [[TMP1:%.*]] = extractelement <vscale x 2 x double> [[DATA:%.*]], i64 1
; CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <vscale x 2 x double> poison, double [[TMP1]], i32 0
declare <vscale x 8 x half> @llvm.aarch64.sve.tbl.nxv8f16( <vscale x 8 x half>, <vscale x 8 x i16>)
declare <vscale x 4 x float> @llvm.aarch64.sve.tbl.nxv4f32( <vscale x 4 x float>, <vscale x 4 x i32>)
declare <vscale x 2 x double> @llvm.aarch64.sve.tbl.nxv2f64( <vscale x 2 x double>, <vscale x 2 x i64>)
+
+attributes #0 = { "target-features"="+sve" }
-; RUN: opt -S -aarch64-sve-intrinsic-opts -mtriple=aarch64-linux-gnu -mattr=+sve < %s | FileCheck --check-prefix OPT %s
+; RUN: opt -S -instcombine < %s | FileCheck %s
-define i1 @ptest_any1(<vscale x 2 x i1> %a) {
-; OPT-LABEL: ptest_any1
-; OPT: %mask = tail call <vscale x 2 x i1> @llvm.aarch64.sve.ptrue.nxv2i1(i32 0)
-; OPT-NOT: convert
-; OPT-NEXT: %[[OUT:.*]] = call i1 @llvm.aarch64.sve.ptest.any.nxv2i1(<vscale x 2 x i1> %mask, <vscale x 2 x i1> %a)
-; OPT-NEXT: ret i1 %[[OUT]]
+target triple = "aarch64-unknown-linux-gnu"
+
+define i1 @ptest_any1(<vscale x 2 x i1> %a) #0 {
+; CHECK-LABEL: ptest_any1
+; CHECK: %mask = tail call <vscale x 2 x i1> @llvm.aarch64.sve.ptrue.nxv2i1(i32 0)
+; CHECK-NOT: convert
+; CHECK-NEXT: %[[OUT:.*]] = call i1 @llvm.aarch64.sve.ptest.any.nxv2i1(<vscale x 2 x i1> %mask, <vscale x 2 x i1> %a)
+; CHECK-NEXT: ret i1 %[[OUT]]
%mask = tail call <vscale x 2 x i1> @llvm.aarch64.sve.ptrue.nxv2i1(i32 0)
%1 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> %mask)
%2 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> %a)
}
; No transform because the ptest is using differently sized operands.
-define i1 @ptest_any2(<vscale x 4 x i1> %a) {
-; OPT-LABEL: ptest_any2
-; OPT: %mask = tail call <vscale x 2 x i1> @llvm.aarch64.sve.ptrue.nxv2i1(i32 31)
-; OPT-NEXT: %1 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> %mask)
-; OPT-NEXT: %2 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> %a)
-; OPT-NEXT: %out = call i1 @llvm.aarch64.sve.ptest.any.nxv16i1(<vscale x 16 x i1> %1, <vscale x 16 x i1> %2)
+define i1 @ptest_any2(<vscale x 4 x i1> %a) #0 {
+; CHECK-LABEL: ptest_any2
+; CHECK: %mask = tail call <vscale x 2 x i1> @llvm.aarch64.sve.ptrue.nxv2i1(i32 31)
+; CHECK-NEXT: %1 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> %mask)
+; CHECK-NEXT: %2 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> %a)
+; CHECK-NEXT: %out = call i1 @llvm.aarch64.sve.ptest.any.nxv16i1(<vscale x 16 x i1> %1, <vscale x 16 x i1> %2)
%mask = tail call <vscale x 2 x i1> @llvm.aarch64.sve.ptrue.nxv2i1(i32 31)
%1 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> %mask)
%2 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> %a)
ret i1 %out
}
-define i1 @ptest_first(<vscale x 4 x i1> %a) {
-; OPT-LABEL: ptest_first
-; OPT: %mask = tail call <vscale x 4 x i1> @llvm.aarch64.sve.ptrue.nxv4i1(i32 0)
-; OPT-NOT: convert
-; OPT-NEXT: %[[OUT:.*]] = call i1 @llvm.aarch64.sve.ptest.first.nxv4i1(<vscale x 4 x i1> %mask, <vscale x 4 x i1> %a)
-; OPT-NEXT: ret i1 %[[OUT]]
+define i1 @ptest_first(<vscale x 4 x i1> %a) #0 {
+; CHECK-LABEL: ptest_first
+; CHECK: %mask = tail call <vscale x 4 x i1> @llvm.aarch64.sve.ptrue.nxv4i1(i32 0)
+; CHECK-NOT: convert
+; CHECK-NEXT: %[[OUT:.*]] = call i1 @llvm.aarch64.sve.ptest.first.nxv4i1(<vscale x 4 x i1> %mask, <vscale x 4 x i1> %a)
+; CHECK-NEXT: ret i1 %[[OUT]]
%mask = tail call <vscale x 4 x i1> @llvm.aarch64.sve.ptrue.nxv4i1(i32 0)
%1 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> %mask)
%2 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> %a)
ret i1 %out
}
-define i1 @ptest_first_same_ops(<vscale x 2 x i1> %a) {
-; OPT-LABEL: ptest_first_same_ops
-; OPT: %[[OUT:.*]] = call i1 @llvm.aarch64.sve.ptest.first.nxv2i1(<vscale x 2 x i1> %a, <vscale x 2 x i1> %a)
-; OPT-NOT: convert
-; OPT-NEXT: ret i1 %[[OUT]]
+define i1 @ptest_first_same_ops(<vscale x 2 x i1> %a) #0 {
+; CHECK-LABEL: ptest_first_same_ops
+; CHECK: %[[OUT:.*]] = call i1 @llvm.aarch64.sve.ptest.first.nxv2i1(<vscale x 2 x i1> %a, <vscale x 2 x i1> %a)
+; CHECK-NOT: convert
+; CHECK-NEXT: ret i1 %[[OUT]]
%1 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> %a)
%2 = tail call i1 @llvm.aarch64.sve.ptest.first.nxv16i1(<vscale x 16 x i1> %1, <vscale x 16 x i1> %1)
ret i1 %2
}
-define i1 @ptest_last(<vscale x 8 x i1> %a) {
-; OPT-LABEL: ptest_last
-; OPT: %mask = tail call <vscale x 8 x i1> @llvm.aarch64.sve.ptrue.nxv8i1(i32 0)
-; OPT-NOT: convert
-; OPT-NEXT: %[[OUT:.*]] = call i1 @llvm.aarch64.sve.ptest.last.nxv8i1(<vscale x 8 x i1> %mask, <vscale x 8 x i1> %a)
-; OPT-NEXT: ret i1 %[[OUT]]
+define i1 @ptest_last(<vscale x 8 x i1> %a) #0 {
+; CHECK-LABEL: ptest_last
+; CHECK: %mask = tail call <vscale x 8 x i1> @llvm.aarch64.sve.ptrue.nxv8i1(i32 0)
+; CHECK-NOT: convert
+; CHECK-NEXT: %[[OUT:.*]] = call i1 @llvm.aarch64.sve.ptest.last.nxv8i1(<vscale x 8 x i1> %mask, <vscale x 8 x i1> %a)
+; CHECK-NEXT: ret i1 %[[OUT]]
%mask = tail call <vscale x 8 x i1> @llvm.aarch64.sve.ptrue.nxv8i1(i32 0)
%1 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %mask)
%2 = tail call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> %a)
declare <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1>)
declare <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1>)
declare <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1>)
+
+attributes #0 = { "target-features"="+sve" }