404 lines · cpp
1//===- llvm/unittests/Transforms/Vectorize/VPlanVerifierTest.cpp ----------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "../lib/Transforms/Vectorize/VPlanVerifier.h"10#include "../lib/Transforms/Vectorize/VPlan.h"11#include "../lib/Transforms/Vectorize/VPlanCFG.h"12#include "VPlanTestBase.h"13#include "llvm/IR/Instruction.h"14#include "llvm/IR/Instructions.h"15#include "gtest/gtest.h"16 17using namespace llvm;18 19using VPVerifierTest = VPlanTestBase;20 21namespace {22TEST_F(VPVerifierTest, VPInstructionUseBeforeDefSameBB) {23 VPlan &Plan = getPlan();24 VPValue *Zero = Plan.getConstantInt(32, 0);25 VPInstruction *DefI = new VPInstruction(Instruction::Add, {Zero, Zero});26 VPInstruction *UseI = new VPInstruction(Instruction::Sub, {DefI, Zero});27 auto *CanIV = new VPCanonicalIVPHIRecipe(Zero, {});28 29 VPBasicBlock *VPBB1 = Plan.getEntry();30 VPBB1->appendRecipe(UseI);31 VPBB1->appendRecipe(DefI);32 33 VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");34 VPBB2->appendRecipe(CanIV);35 VPRegionBlock *R1 = Plan.createLoopRegion("R1", VPBB2, VPBB2);36 VPBlockUtils::connectBlocks(VPBB1, R1);37 VPBlockUtils::connectBlocks(R1, Plan.getScalarHeader());38 39#if GTEST_HAS_STREAM_REDIRECTION40 ::testing::internal::CaptureStderr();41#endif42 EXPECT_FALSE(verifyVPlanIsValid(Plan));43#if GTEST_HAS_STREAM_REDIRECTION44#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)45 EXPECT_STREQ("Use before def!\n"46 " EMIT vp<%1> = sub vp<%2>, ir<0>\n"47 " before\n"48 " EMIT vp<%2> = add ir<0>, ir<0>\n",49 ::testing::internal::GetCapturedStderr().c_str());50#else51 EXPECT_STREQ("Use before def!\n",52 ::testing::internal::GetCapturedStderr().c_str());53#endif54#endif55}56 57TEST_F(VPVerifierTest, VPInstructionUseBeforeDefDifferentBB) {58 VPlan &Plan = getPlan();59 VPValue *Zero = Plan.getConstantInt(32, 0);60 VPInstruction *DefI = new VPInstruction(Instruction::Add, {Zero, Zero});61 VPInstruction *UseI = new VPInstruction(Instruction::Sub, {DefI, Zero});62 auto *CanIV = new VPCanonicalIVPHIRecipe(Zero, {});63 VPInstruction *BranchOnCond =64 new VPInstruction(VPInstruction::BranchOnCond, {CanIV});65 66 VPBasicBlock *VPBB1 = Plan.getEntry();67 VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");68 69 VPBB1->appendRecipe(UseI);70 VPBB2->appendRecipe(CanIV);71 VPBB2->appendRecipe(DefI);72 VPBB2->appendRecipe(BranchOnCond);73 74 VPRegionBlock *R1 = Plan.createLoopRegion("R1", VPBB2, VPBB2);75 VPBlockUtils::connectBlocks(VPBB1, R1);76 VPBlockUtils::connectBlocks(R1, Plan.getScalarHeader());77 78#if GTEST_HAS_STREAM_REDIRECTION79 ::testing::internal::CaptureStderr();80#endif81 EXPECT_FALSE(verifyVPlanIsValid(Plan));82#if GTEST_HAS_STREAM_REDIRECTION83#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)84 EXPECT_STREQ("Use before def!\n"85 " EMIT vp<%1> = sub vp<%3>, ir<0>\n"86 " before\n"87 " EMIT vp<%3> = add ir<0>, ir<0>\n",88 ::testing::internal::GetCapturedStderr().c_str());89#else90 EXPECT_STREQ("Use before def!\n",91 ::testing::internal::GetCapturedStderr().c_str());92#endif93#endif94}95 96TEST_F(VPVerifierTest, VPBlendUseBeforeDefDifferentBB) {97 VPlan &Plan = getPlan();98 IntegerType *Int32 = IntegerType::get(C, 32);99 auto *Phi = PHINode::Create(Int32, 1);100 VPValue *Zero = Plan.getOrAddLiveIn(ConstantInt::get(Int32, 0));101 102 VPInstruction *DefI = new VPInstruction(Instruction::Add, {Zero, Zero});103 auto *CanIV = new VPCanonicalIVPHIRecipe(Zero, {});104 VPInstruction *BranchOnCond =105 new VPInstruction(VPInstruction::BranchOnCond, {CanIV});106 auto *Blend = new VPBlendRecipe(Phi, {DefI}, {});107 108 VPBasicBlock *VPBB1 = Plan.getEntry();109 VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");110 VPBasicBlock *VPBB3 = Plan.createVPBasicBlock("");111 VPBasicBlock *VPBB4 = Plan.createVPBasicBlock("");112 113 VPBB2->appendRecipe(CanIV);114 VPBB3->appendRecipe(Blend);115 VPBB4->appendRecipe(DefI);116 VPBB4->appendRecipe(BranchOnCond);117 118 VPBlockUtils::connectBlocks(VPBB2, VPBB3);119 VPBlockUtils::connectBlocks(VPBB3, VPBB4);120 VPRegionBlock *R1 = Plan.createLoopRegion("R1", VPBB2, VPBB4);121 VPBlockUtils::connectBlocks(VPBB1, R1);122 VPBB3->setParent(R1);123 124 VPBlockUtils::connectBlocks(R1, Plan.getScalarHeader());125 126#if GTEST_HAS_STREAM_REDIRECTION127 ::testing::internal::CaptureStderr();128#endif129 EXPECT_FALSE(verifyVPlanIsValid(Plan));130#if GTEST_HAS_STREAM_REDIRECTION131#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)132 EXPECT_STREQ("Use before def!\n"133 " BLEND ir<<badref>> = vp<%2>\n"134 " before\n"135 " EMIT vp<%2> = add ir<0>, ir<0>\n",136 ::testing::internal::GetCapturedStderr().c_str());137#else138 EXPECT_STREQ("Use before def!\n",139 ::testing::internal::GetCapturedStderr().c_str());140#endif141#endif142 143 delete Phi;144}145 146TEST_F(VPVerifierTest, VPPhiIncomingValueDoesntDominateIncomingBlock) {147 VPlan &Plan = getPlan();148 IntegerType *Int32 = IntegerType::get(C, 32);149 VPValue *Zero = Plan.getOrAddLiveIn(ConstantInt::get(Int32, 0));150 151 VPBasicBlock *VPBB1 = Plan.getEntry();152 VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");153 VPBasicBlock *VPBB3 = Plan.createVPBasicBlock("");154 VPBasicBlock *VPBB4 = Plan.createVPBasicBlock("");155 156 VPInstruction *DefI = new VPInstruction(Instruction::Add, {Zero, Zero});157 VPPhi *Phi = new VPPhi({DefI}, {});158 VPBB2->appendRecipe(Phi);159 VPBB2->appendRecipe(DefI);160 auto *CanIV = new VPCanonicalIVPHIRecipe(Zero, {});161 VPBB3->appendRecipe(CanIV);162 163 VPRegionBlock *R1 = Plan.createLoopRegion("R1", VPBB3, VPBB3);164 VPBlockUtils::connectBlocks(VPBB1, VPBB2);165 VPBlockUtils::connectBlocks(VPBB2, R1);166 VPBlockUtils::connectBlocks(VPBB4, Plan.getScalarHeader());167#if GTEST_HAS_STREAM_REDIRECTION168 ::testing::internal::CaptureStderr();169#endif170 EXPECT_FALSE(verifyVPlanIsValid(Plan));171#if GTEST_HAS_STREAM_REDIRECTION172#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)173 EXPECT_STREQ("Incoming def does not dominate incoming block!\n"174 " EMIT vp<%2> = add ir<0>, ir<0>\n"175 " does not dominate preheader for\n"176 " EMIT-SCALAR vp<%1> = phi [ vp<%2>, preheader ]",177 ::testing::internal::GetCapturedStderr().c_str());178#else179 EXPECT_STREQ("Incoming def does not dominate incoming block!\n",180 ::testing::internal::GetCapturedStderr().c_str());181#endif182#endif183}184 185TEST_F(VPVerifierTest, DuplicateSuccessorsOutsideRegion) {186 VPlan &Plan = getPlan();187 VPValue *Zero = Plan.getConstantInt(32, 0);188 VPInstruction *I1 = new VPInstruction(Instruction::Add, {Zero, Zero});189 auto *CanIV = new VPCanonicalIVPHIRecipe(Zero, {});190 VPInstruction *BranchOnCond =191 new VPInstruction(VPInstruction::BranchOnCond, {CanIV});192 VPInstruction *BranchOnCond2 =193 new VPInstruction(VPInstruction::BranchOnCond, {I1});194 195 VPBasicBlock *VPBB1 = Plan.getEntry();196 VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");197 198 VPBB1->appendRecipe(I1);199 VPBB1->appendRecipe(BranchOnCond2);200 VPBB2->appendRecipe(CanIV);201 VPBB2->appendRecipe(BranchOnCond);202 203 VPRegionBlock *R1 = Plan.createLoopRegion("R1", VPBB2, VPBB2);204 VPBlockUtils::connectBlocks(VPBB1, R1);205 VPBlockUtils::connectBlocks(VPBB1, R1);206 207 VPBlockUtils::connectBlocks(R1, Plan.getScalarHeader());208 209#if GTEST_HAS_STREAM_REDIRECTION210 ::testing::internal::CaptureStderr();211#endif212 EXPECT_FALSE(verifyVPlanIsValid(Plan));213#if GTEST_HAS_STREAM_REDIRECTION214 EXPECT_STREQ("Multiple instances of the same successor.\n",215 ::testing::internal::GetCapturedStderr().c_str());216#endif217}218 219TEST_F(VPVerifierTest, DuplicateSuccessorsInsideRegion) {220 VPlan &Plan = getPlan();221 VPValue *Zero = Plan.getConstantInt(32, 0);222 VPInstruction *I1 = new VPInstruction(Instruction::Add, {Zero, Zero});223 auto *CanIV = new VPCanonicalIVPHIRecipe(Zero, {});224 VPInstruction *BranchOnCond =225 new VPInstruction(VPInstruction::BranchOnCond, {CanIV});226 VPInstruction *BranchOnCond2 =227 new VPInstruction(VPInstruction::BranchOnCond, {I1});228 229 VPBasicBlock *VPBB1 = Plan.getEntry();230 VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");231 VPBasicBlock *VPBB3 = Plan.createVPBasicBlock("");232 233 VPBB1->appendRecipe(I1);234 VPBB2->appendRecipe(CanIV);235 VPBB2->appendRecipe(BranchOnCond2);236 VPBB3->appendRecipe(BranchOnCond);237 238 VPBlockUtils::connectBlocks(VPBB2, VPBB3);239 VPBlockUtils::connectBlocks(VPBB2, VPBB3);240 VPRegionBlock *R1 = Plan.createLoopRegion("R1", VPBB2, VPBB3);241 VPBlockUtils::connectBlocks(VPBB1, R1);242 VPBB3->setParent(R1);243 244 VPBlockUtils::connectBlocks(R1, Plan.getScalarHeader());245 246#if GTEST_HAS_STREAM_REDIRECTION247 ::testing::internal::CaptureStderr();248#endif249 EXPECT_FALSE(verifyVPlanIsValid(Plan));250#if GTEST_HAS_STREAM_REDIRECTION251 EXPECT_STREQ("Multiple instances of the same successor.\n",252 ::testing::internal::GetCapturedStderr().c_str());253#endif254}255 256TEST_F(VPVerifierTest, BlockOutsideRegionWithParent) {257 VPlan &Plan = getPlan();258 259 VPBasicBlock *VPBB1 = Plan.getEntry();260 VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");261 262 VPValue *Zero = Plan.getConstantInt(32, 0);263 auto *CanIV = new VPCanonicalIVPHIRecipe(Zero, {});264 VPBB2->appendRecipe(CanIV);265 266 VPInstruction *DefI = new VPInstruction(Instruction::Add, {Zero, Zero});267 VPInstruction *BranchOnCond =268 new VPInstruction(VPInstruction::BranchOnCond, {DefI});269 270 VPBB1->appendRecipe(DefI);271 VPBB2->appendRecipe(BranchOnCond);272 273 VPRegionBlock *R1 = Plan.createLoopRegion("R1", VPBB2, VPBB2);274 VPBlockUtils::connectBlocks(VPBB1, R1);275 276 VPBlockUtils::connectBlocks(R1, Plan.getScalarHeader());277 VPBB1->setParent(R1);278 279#if GTEST_HAS_STREAM_REDIRECTION280 ::testing::internal::CaptureStderr();281#endif282 EXPECT_FALSE(verifyVPlanIsValid(Plan));283#if GTEST_HAS_STREAM_REDIRECTION284 EXPECT_STREQ("Predecessor is not in the same region.\n",285 ::testing::internal::GetCapturedStderr().c_str());286#endif287}288 289TEST_F(VPVerifierTest, NonHeaderPHIInHeader) {290 VPlan &Plan = getPlan();291 VPValue *Zero = Plan.getConstantInt(32, 0);292 auto *CanIV = new VPCanonicalIVPHIRecipe(Zero, {});293 auto *BranchOnCond = new VPInstruction(VPInstruction::BranchOnCond, {CanIV});294 295 VPBasicBlock *VPBB1 = Plan.getEntry();296 VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("header");297 298 VPBB2->appendRecipe(CanIV);299 300 PHINode *PHINode = PHINode::Create(Type::getInt32Ty(C), 2);301 auto *IRPhi = new VPIRPhi(*PHINode);302 VPBB2->appendRecipe(IRPhi);303 VPBB2->appendRecipe(BranchOnCond);304 305 VPRegionBlock *R1 = Plan.createLoopRegion("R1", VPBB2, VPBB2);306 VPBlockUtils::connectBlocks(VPBB1, R1);307 VPBlockUtils::connectBlocks(R1, Plan.getScalarHeader());308 309#if GTEST_HAS_STREAM_REDIRECTION310 ::testing::internal::CaptureStderr();311#endif312 EXPECT_FALSE(verifyVPlanIsValid(Plan));313#if GTEST_HAS_STREAM_REDIRECTION314#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)315 EXPECT_STREQ(316 "Found non-header PHI recipe in header VPBB: IR <badref> = phi i32 \n",317 ::testing::internal::GetCapturedStderr().c_str());318#else319 EXPECT_STREQ("Found non-header PHI recipe in header VPBB",320 ::testing::internal::GetCapturedStderr().c_str());321#endif322#endif323 324 delete PHINode;325}326 327class VPIRVerifierTest : public VPlanTestIRBase {};328 329TEST_F(VPIRVerifierTest, testVerifyIRPhiInScalarHeaderVPIRBB) {330 const char *ModuleString =331 "define void @f(ptr %A, i64 %N) {\n"332 "entry:\n"333 " br label %loop\n"334 "loop:\n"335 " %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]\n"336 " %iv.next = add i64 %iv, 1\n"337 " %exitcond = icmp ne i64 %iv.next, %N\n"338 " br i1 %exitcond, label %loop, label %for.end\n"339 "for.end:\n"340 " %p = phi i64 [ %iv, %loop ]\n"341 " ret void\n"342 "}\n";343 344 Module &M = parseModule(ModuleString);345 346 Function *F = M.getFunction("f");347 BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();348 auto Plan = buildVPlan(LoopHeader);349 VPValue *Zero = Plan->getConstantInt(32, 0);350 Plan->getScalarHeader()->front().addOperand(Zero);351 352#if GTEST_HAS_STREAM_REDIRECTION353 ::testing::internal::CaptureStderr();354#endif355 EXPECT_FALSE(verifyVPlanIsValid(*Plan));356#if GTEST_HAS_STREAM_REDIRECTION357 EXPECT_STREQ(358 "Phi-like recipe with different number of operands and predecessors.\n",359 ::testing::internal::GetCapturedStderr().c_str());360#endif361}362 363TEST_F(VPIRVerifierTest, testVerifyIRPhiInExitVPIRBB) {364 const char *ModuleString =365 "define void @f(ptr %A, i64 %N) {\n"366 "entry:\n"367 " br label %loop\n"368 "loop:\n"369 " %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]\n"370 " %iv.next = add i64 %iv, 1\n"371 " %exitcond = icmp ne i64 %iv.next, %N\n"372 " br i1 %exitcond, label %loop, label %for.end\n"373 "for.end:\n"374 " %p = phi i64 [ %iv, %loop ]\n"375 " ret void\n"376 "}\n";377 378 Module &M = parseModule(ModuleString);379 380 Function *F = M.getFunction("f");381 BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();382 auto Plan = buildVPlan(LoopHeader);383 384 // Create a definition in the vector loop header that will be used by the phi.385 auto *HeaderBlock =386 cast<VPBasicBlock>(Plan->getVectorLoopRegion()->getEntry());387 VPInstruction *DefI =388 new VPInstruction(VPInstruction::ExtractLastElement,389 {HeaderBlock->front().getVPSingleValue()});390 DefI->insertBefore(Plan->getMiddleBlock()->getTerminator());391 Plan->getExitBlocks()[0]->front().addOperand(DefI);392 393#if GTEST_HAS_STREAM_REDIRECTION394 ::testing::internal::CaptureStderr();395#endif396 EXPECT_FALSE(verifyVPlanIsValid(*Plan));397#if GTEST_HAS_STREAM_REDIRECTION398 EXPECT_STREQ(399 "Phi-like recipe with different number of operands and predecessors.\n",400 ::testing::internal::GetCapturedStderr().c_str());401#endif402}403} // namespace404