diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h --- a/llvm/lib/Transforms/Vectorize/VPlan.h +++ b/llvm/lib/Transforms/Vectorize/VPlan.h @@ -681,6 +681,9 @@ // All VPDefs are also VPRecipeBases. return true; } + + /// Returns true if the recipe may have side-effects. + bool mayHaveSideEffects() const; }; inline bool VPUser::classof(const VPDef *Def) { diff --git a/llvm/lib/Transforms/Vectorize/VPlan.cpp b/llvm/lib/Transforms/Vectorize/VPlan.cpp --- a/llvm/lib/Transforms/Vectorize/VPlan.cpp +++ b/llvm/lib/Transforms/Vectorize/VPlan.cpp @@ -510,6 +510,31 @@ } #endif +bool VPRecipeBase::mayHaveSideEffects() const { + switch (getVPDefID()) { + case VPBranchOnMaskSC: + return false; + case VPBlendSC: + case VPWidenSC: + case VPWidenGEPSC: + case VPReductionSC: + case VPWidenSelectSC: { + const Instruction *I = + dyn_cast_or_null(getVPValue()->getUnderlyingValue()); + (void)I; + assert((!I || !I->mayHaveSideEffects()) && + "underlying instruction has side-effects"); + return false; + } + case VPReplicateSC: { + auto *R = cast(this); + return R->getUnderlyingInstr()->mayHaveSideEffects(); + } + default: + return true; + } +} + void VPRecipeBase::insertBefore(VPRecipeBase *InsertPos) { assert(!Parent && "Recipe already in some VPBasicBlock"); assert(InsertPos->getParent() && diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp --- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp +++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp @@ -610,6 +610,103 @@ delete Load; } +TEST(VPRecipeTest, MayHaveSideEffects) { + LLVMContext C; + IntegerType *Int1 = IntegerType::get(C, 1); + IntegerType *Int32 = IntegerType::get(C, 32); + PointerType *Int32Ptr = PointerType::get(Int32, 0); + + { + auto *AI = BinaryOperator::CreateAdd(UndefValue::get(Int32), + UndefValue::get(Int32)); + VPValue Op1; + VPValue Op2; + SmallVector Args; + Args.push_back(&Op1); + Args.push_back(&Op1); + VPWidenRecipe Recipe(*AI, make_range(Args.begin(), Args.end())); + EXPECT_FALSE(Recipe.mayHaveSideEffects()); + + delete AI; + } + + { + auto *SelectI = SelectInst::Create( + UndefValue::get(Int1), UndefValue::get(Int32), UndefValue::get(Int32)); + VPValue Op1; + VPValue Op2; + VPValue Op3; + SmallVector Args; + Args.push_back(&Op1); + Args.push_back(&Op2); + Args.push_back(&Op3); + VPWidenSelectRecipe Recipe(*SelectI, make_range(Args.begin(), Args.end()), + false); + EXPECT_FALSE(Recipe.mayHaveSideEffects()); + delete SelectI; + } + + { + auto *GEP = GetElementPtrInst::Create(Int32, UndefValue::get(Int32Ptr), + UndefValue::get(Int32)); + VPValue Op1; + VPValue Op2; + SmallVector Args; + Args.push_back(&Op1); + Args.push_back(&Op2); + VPWidenGEPRecipe Recipe(GEP, make_range(Args.begin(), Args.end())); + EXPECT_FALSE(Recipe.mayHaveSideEffects()); + delete GEP; + } + + { + VPValue Mask; + VPBranchOnMaskRecipe Recipe(&Mask); + EXPECT_FALSE(Recipe.mayHaveSideEffects()); + } + + { + VPValue ChainOp; + VPValue VecOp; + VPValue CondOp; + VPReductionRecipe Recipe(nullptr, nullptr, &ChainOp, &CondOp, &VecOp, + nullptr); + EXPECT_FALSE(Recipe.mayHaveSideEffects()); + } + + { + auto *Load = + new LoadInst(Int32, UndefValue::get(Int32Ptr), "", false, Align(1)); + VPValue Addr; + VPValue Mask; + VPWidenMemoryInstructionRecipe Recipe(*Load, &Addr, &Mask); + EXPECT_TRUE(Recipe.mayHaveSideEffects()); + + delete Load; + } + + { + FunctionType *FTy = FunctionType::get(Int32, false); + auto *Call = CallInst::Create(FTy, UndefValue::get(FTy)); + VPValue Op1; + VPValue Op2; + SmallVector Args; + Args.push_back(&Op1); + Args.push_back(&Op2); + VPWidenCallRecipe Recipe(*Call, make_range(Args.begin(), Args.end())); + EXPECT_TRUE(Recipe.mayHaveSideEffects()); + delete Call; + } + + // The initial implementation is conservative with respect to VPInstructions. + { + VPValue Op1; + VPValue Op2; + VPInstruction Recipe(Instruction::Add, {&Op1, &Op2}); + EXPECT_TRUE(Recipe.mayHaveSideEffects()); + } +} + #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) TEST(VPRecipeTest, dump) { VPlan Plan;