Files
clang-p2996/llvm/unittests/Transforms/Vectorize/VPlanHCFGTest.cpp
Florian Hahn b021464d35 [VPlan] Introduce scalar loop header in plan, remove VPLiveOut. (#109975)
Update VPlan to include the scalar loop header. This allows retiring
VPLiveOut, as the remaining live-outs can now be handled by adding
operands to the wrapped phis in the scalar loop header.

Note that the current version only includes the scalar loop header, no
other loop blocks and also does not wrap it in a region block.

PR: https://github.com/llvm/llvm-project/pull/109975
2024-10-31 21:36:44 +01:00

229 lines
8.4 KiB
C++

//===- llvm/unittest/Transforms/Vectorize/VPlanHCFGTest.cpp ---------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "../lib/Transforms/Vectorize/VPlan.h"
#include "../lib/Transforms/Vectorize/VPlanTransforms.h"
#include "VPlanTestBase.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/TargetParser/Triple.h"
#include "gtest/gtest.h"
#include <string>
namespace llvm {
namespace {
class VPlanHCFGTest : public VPlanTestBase {};
TEST_F(VPlanHCFGTest, testBuildHCFGInnerLoop) {
const char *ModuleString =
"define void @f(ptr %A, i64 %N) {\n"
"entry:\n"
" br label %for.body\n"
"for.body:\n"
" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
" %arr.idx = getelementptr inbounds i32, ptr %A, i64 %indvars.iv\n"
" %l1 = load i32, ptr %arr.idx, align 4\n"
" %res = add i32 %l1, 10\n"
" store i32 %res, ptr %arr.idx, align 4\n"
" %indvars.iv.next = add i64 %indvars.iv, 1\n"
" %exitcond = icmp ne i64 %indvars.iv.next, %N\n"
" br i1 %exitcond, label %for.body, label %for.end\n"
"for.end:\n"
" ret void\n"
"}\n";
Module &M = parseModule(ModuleString);
Function *F = M.getFunction("f");
BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
auto Plan = buildHCFG(LoopHeader);
VPBasicBlock *Entry = Plan->getEntry()->getEntryBasicBlock();
EXPECT_NE(nullptr, Entry->getSingleSuccessor());
EXPECT_EQ(0u, Entry->getNumPredecessors());
EXPECT_EQ(1u, Entry->getNumSuccessors());
// Check that the region following the preheader is a single basic-block
// region (loop).
VPBasicBlock *VecBB = Plan->getVectorLoopRegion()->getEntryBasicBlock();
EXPECT_EQ(8u, VecBB->size());
EXPECT_EQ(0u, VecBB->getNumPredecessors());
EXPECT_EQ(0u, VecBB->getNumSuccessors());
EXPECT_EQ(VecBB->getParent()->getEntryBasicBlock(), VecBB);
EXPECT_EQ(VecBB->getParent()->getExitingBasicBlock(), VecBB);
EXPECT_EQ(&*Plan, VecBB->getPlan());
auto Iter = VecBB->begin();
VPWidenPHIRecipe *Phi = dyn_cast<VPWidenPHIRecipe>(&*Iter++);
EXPECT_NE(nullptr, Phi);
VPInstruction *Idx = dyn_cast<VPInstruction>(&*Iter++);
EXPECT_EQ(Instruction::GetElementPtr, Idx->getOpcode());
EXPECT_EQ(2u, Idx->getNumOperands());
EXPECT_EQ(Phi, Idx->getOperand(1));
VPInstruction *Load = dyn_cast<VPInstruction>(&*Iter++);
EXPECT_EQ(Instruction::Load, Load->getOpcode());
EXPECT_EQ(1u, Load->getNumOperands());
EXPECT_EQ(Idx, Load->getOperand(0));
VPInstruction *Add = dyn_cast<VPInstruction>(&*Iter++);
EXPECT_EQ(Instruction::Add, Add->getOpcode());
EXPECT_EQ(2u, Add->getNumOperands());
EXPECT_EQ(Load, Add->getOperand(0));
VPInstruction *Store = dyn_cast<VPInstruction>(&*Iter++);
EXPECT_EQ(Instruction::Store, Store->getOpcode());
EXPECT_EQ(2u, Store->getNumOperands());
EXPECT_EQ(Add, Store->getOperand(0));
EXPECT_EQ(Idx, Store->getOperand(1));
VPInstruction *IndvarAdd = dyn_cast<VPInstruction>(&*Iter++);
EXPECT_EQ(Instruction::Add, IndvarAdd->getOpcode());
EXPECT_EQ(2u, IndvarAdd->getNumOperands());
EXPECT_EQ(Phi, IndvarAdd->getOperand(0));
VPInstruction *ICmp = dyn_cast<VPInstruction>(&*Iter++);
EXPECT_EQ(Instruction::ICmp, ICmp->getOpcode());
EXPECT_EQ(2u, ICmp->getNumOperands());
EXPECT_EQ(IndvarAdd, ICmp->getOperand(0));
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
// Add an external value to check we do not print the list of external values,
// as this is not required with the new printing.
Plan->getOrAddLiveIn(&*F->arg_begin());
std::string FullDump;
raw_string_ostream OS(FullDump);
Plan->printDOT(OS);
const char *ExpectedStr = R"(digraph VPlan {
graph [labelloc=t, fontsize=30; label="Vectorization Plan\n for UF\>=1\nLive-in vp\<%0\> = vector-trip-count\nLive-in ir\<%N\> = original trip-count\n"]
node [shape=rect, fontname=Courier, fontsize=30]
edge [fontname=Courier, fontsize=30]
compound=true
N0 [label =
"ir-bb\<entry\>:\l" +
"No successors\l"
]
N1 [label =
"vector.ph:\l" +
"Successor(s): vector loop\l"
]
N1 -> N2 [ label="" lhead=cluster_N3]
subgraph cluster_N3 {
fontname=Courier
label="\<x1\> vector loop"
N2 [label =
"vector.body:\l" +
" WIDEN-PHI ir\<%indvars.iv\> = phi ir\<0\>, ir\<%indvars.iv.next\>\l" +
" EMIT ir\<%arr.idx\> = getelementptr ir\<%A\>, ir\<%indvars.iv\>\l" +
" EMIT ir\<%l1\> = load ir\<%arr.idx\>\l" +
" EMIT ir\<%res\> = add ir\<%l1\>, ir\<10\>\l" +
" EMIT store ir\<%res\>, ir\<%arr.idx\>\l" +
" EMIT ir\<%indvars.iv.next\> = add ir\<%indvars.iv\>, ir\<1\>\l" +
" EMIT ir\<%exitcond\> = icmp ir\<%indvars.iv.next\>, ir\<%N\>\l" +
" EMIT branch-on-cond ir\<%exitcond\>\l" +
"No successors\l"
]
}
N2 -> N4 [ label="" ltail=cluster_N3]
N4 [label =
"middle.block:\l" +
" EMIT vp\<%cmp.n\> = icmp eq ir\<%N\>, vp\<%0\>\l" +
" EMIT branch-on-cond vp\<%cmp.n\>\l" +
"Successor(s): ir-bb\<for.end\>, scalar.ph\l"
]
N4 -> N5 [ label="T"]
N4 -> N6 [ label="F"]
N5 [label =
"ir-bb\<for.end\>:\l" +
"No successors\l"
]
N6 [label =
"scalar.ph:\l" +
"Successor(s): ir-bb\<for.body\>\l"
]
N6 -> N7 [ label=""]
N7 [label =
"ir-bb\<for.body\>:\l" +
" IR %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\l" +
" IR %arr.idx = getelementptr inbounds i32, ptr %A, i64 %indvars.iv\l" +
" IR %l1 = load i32, ptr %arr.idx, align 4\l" +
" IR %res = add i32 %l1, 10\l" +
" IR store i32 %res, ptr %arr.idx, align 4\l" +
" IR %indvars.iv.next = add i64 %indvars.iv, 1\l" +
" IR %exitcond = icmp ne i64 %indvars.iv.next, %N\l" +
"No successors\l"
]
}
)";
EXPECT_EQ(ExpectedStr, FullDump);
#endif
TargetLibraryInfoImpl TLII(Triple(M.getTargetTriple()));
TargetLibraryInfo TLI(TLII);
VPlanTransforms::VPInstructionsToVPRecipes(
Plan, [](PHINode *P) { return nullptr; }, *SE, TLI);
}
TEST_F(VPlanHCFGTest, testVPInstructionToVPRecipesInner) {
const char *ModuleString =
"define void @f(ptr %A, i64 %N) {\n"
"entry:\n"
" br label %for.body\n"
"for.body:\n"
" %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
" %arr.idx = getelementptr inbounds i32, ptr %A, i64 %indvars.iv\n"
" %l1 = load i32, ptr %arr.idx, align 4\n"
" %res = add i32 %l1, 10\n"
" store i32 %res, ptr %arr.idx, align 4\n"
" %indvars.iv.next = add i64 %indvars.iv, 1\n"
" %exitcond = icmp ne i64 %indvars.iv.next, %N\n"
" br i1 %exitcond, label %for.body, label %for.end\n"
"for.end:\n"
" ret void\n"
"}\n";
Module &M = parseModule(ModuleString);
Function *F = M.getFunction("f");
BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
auto Plan = buildHCFG(LoopHeader);
TargetLibraryInfoImpl TLII(Triple(M.getTargetTriple()));
TargetLibraryInfo TLI(TLII);
VPlanTransforms::VPInstructionsToVPRecipes(
Plan, [](PHINode *P) { return nullptr; }, *SE, TLI);
VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();
EXPECT_NE(nullptr, Entry->getSingleSuccessor());
EXPECT_EQ(0u, Entry->getNumPredecessors());
EXPECT_EQ(1u, Entry->getNumSuccessors());
// Check that the region following the preheader is a single basic-block
// region (loop).
VPBasicBlock *VecBB = Plan->getVectorLoopRegion()->getEntryBasicBlock();
EXPECT_EQ(8u, VecBB->size());
EXPECT_EQ(0u, VecBB->getNumPredecessors());
EXPECT_EQ(0u, VecBB->getNumSuccessors());
EXPECT_EQ(VecBB->getParent()->getEntryBasicBlock(), VecBB);
EXPECT_EQ(VecBB->getParent()->getExitingBasicBlock(), VecBB);
auto Iter = VecBB->begin();
EXPECT_NE(nullptr, dyn_cast<VPWidenPHIRecipe>(&*Iter++));
EXPECT_NE(nullptr, dyn_cast<VPWidenGEPRecipe>(&*Iter++));
EXPECT_NE(nullptr, dyn_cast<VPWidenMemoryRecipe>(&*Iter++));
EXPECT_NE(nullptr, dyn_cast<VPWidenRecipe>(&*Iter++));
EXPECT_NE(nullptr, dyn_cast<VPWidenMemoryRecipe>(&*Iter++));
EXPECT_NE(nullptr, dyn_cast<VPWidenRecipe>(&*Iter++));
EXPECT_NE(nullptr, dyn_cast<VPWidenRecipe>(&*Iter++));
EXPECT_NE(nullptr, dyn_cast<VPInstruction>(&*Iter++));
EXPECT_EQ(VecBB->end(), Iter);
}
} // namespace
} // namespace llvm