1 //===- llvm/unittest/Transforms/Vectorize/VPlanHCFGTest.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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "../lib/Transforms/Vectorize/VPlan.h" 10 #include "../lib/Transforms/Vectorize/VPlanTransforms.h" 11 #include "VPlanTestBase.h" 12 #include "llvm/Analysis/TargetLibraryInfo.h" 13 #include "llvm/TargetParser/Triple.h" 14 #include "gtest/gtest.h" 15 #include <string> 16 17 namespace llvm { 18 namespace { 19 20 class VPlanHCFGTest : public VPlanTestIRBase {}; 21 22 TEST_F(VPlanHCFGTest, testBuildHCFGInnerLoop) { 23 const char *ModuleString = 24 "define void @f(ptr %A, i64 %N) {\n" 25 "entry:\n" 26 " br label %for.body\n" 27 "for.body:\n" 28 " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n" 29 " %arr.idx = getelementptr inbounds i32, ptr %A, i64 %indvars.iv\n" 30 " %l1 = load i32, ptr %arr.idx, align 4\n" 31 " %res = add i32 %l1, 10\n" 32 " store i32 %res, ptr %arr.idx, align 4\n" 33 " %indvars.iv.next = add i64 %indvars.iv, 1\n" 34 " %exitcond = icmp ne i64 %indvars.iv.next, %N\n" 35 " br i1 %exitcond, label %for.body, label %for.end\n" 36 "for.end:\n" 37 " ret void\n" 38 "}\n"; 39 40 Module &M = parseModule(ModuleString); 41 42 Function *F = M.getFunction("f"); 43 BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor(); 44 auto Plan = buildHCFG(LoopHeader); 45 46 VPBasicBlock *Entry = Plan->getEntry()->getEntryBasicBlock(); 47 EXPECT_NE(nullptr, Entry->getSingleSuccessor()); 48 EXPECT_EQ(0u, Entry->getNumPredecessors()); 49 EXPECT_EQ(1u, Entry->getNumSuccessors()); 50 51 // Check that the region following the preheader consists of a block for the 52 // original header and a separate latch. 53 VPBasicBlock *VecBB = Plan->getVectorLoopRegion()->getEntryBasicBlock(); 54 EXPECT_EQ(7u, VecBB->size()); 55 EXPECT_EQ(0u, VecBB->getNumPredecessors()); 56 EXPECT_EQ(1u, VecBB->getNumSuccessors()); 57 EXPECT_EQ(VecBB->getParent()->getEntryBasicBlock(), VecBB); 58 EXPECT_EQ(&*Plan, VecBB->getPlan()); 59 60 VPBlockBase *VecLatch = VecBB->getSingleSuccessor(); 61 EXPECT_EQ(VecLatch->getParent()->getExitingBasicBlock(), VecLatch); 62 EXPECT_EQ(0u, VecLatch->getNumSuccessors()); 63 64 auto Iter = VecBB->begin(); 65 VPWidenPHIRecipe *Phi = dyn_cast<VPWidenPHIRecipe>(&*Iter++); 66 EXPECT_NE(nullptr, Phi); 67 68 VPInstruction *Idx = dyn_cast<VPInstruction>(&*Iter++); 69 EXPECT_EQ(Instruction::GetElementPtr, Idx->getOpcode()); 70 EXPECT_EQ(2u, Idx->getNumOperands()); 71 EXPECT_EQ(Phi, Idx->getOperand(1)); 72 73 VPInstruction *Load = dyn_cast<VPInstruction>(&*Iter++); 74 EXPECT_EQ(Instruction::Load, Load->getOpcode()); 75 EXPECT_EQ(1u, Load->getNumOperands()); 76 EXPECT_EQ(Idx, Load->getOperand(0)); 77 78 VPInstruction *Add = dyn_cast<VPInstruction>(&*Iter++); 79 EXPECT_EQ(Instruction::Add, Add->getOpcode()); 80 EXPECT_EQ(2u, Add->getNumOperands()); 81 EXPECT_EQ(Load, Add->getOperand(0)); 82 83 VPInstruction *Store = dyn_cast<VPInstruction>(&*Iter++); 84 EXPECT_EQ(Instruction::Store, Store->getOpcode()); 85 EXPECT_EQ(2u, Store->getNumOperands()); 86 EXPECT_EQ(Add, Store->getOperand(0)); 87 EXPECT_EQ(Idx, Store->getOperand(1)); 88 89 VPInstruction *IndvarAdd = dyn_cast<VPInstruction>(&*Iter++); 90 EXPECT_EQ(Instruction::Add, IndvarAdd->getOpcode()); 91 EXPECT_EQ(2u, IndvarAdd->getNumOperands()); 92 EXPECT_EQ(Phi, IndvarAdd->getOperand(0)); 93 94 VPInstruction *ICmp = dyn_cast<VPInstruction>(&*Iter++); 95 EXPECT_EQ(Instruction::ICmp, ICmp->getOpcode()); 96 EXPECT_EQ(2u, ICmp->getNumOperands()); 97 EXPECT_EQ(IndvarAdd, ICmp->getOperand(0)); 98 99 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 100 // Add an external value to check we do not print the list of external values, 101 // as this is not required with the new printing. 102 Plan->getOrAddLiveIn(&*F->arg_begin()); 103 std::string FullDump; 104 raw_string_ostream OS(FullDump); 105 Plan->printDOT(OS); 106 const char *ExpectedStr = R"(digraph VPlan { 107 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"] 108 node [shape=rect, fontname=Courier, fontsize=30] 109 edge [fontname=Courier, fontsize=30] 110 compound=true 111 N0 [label = 112 "ir-bb\<entry\>:\l" + 113 "Successor(s): vector.ph\l" 114 ] 115 N0 -> N1 [ label=""] 116 N1 [label = 117 "vector.ph:\l" + 118 "Successor(s): vector loop\l" 119 ] 120 N1 -> N2 [ label="" lhead=cluster_N3] 121 subgraph cluster_N3 { 122 fontname=Courier 123 label="\<x1\> vector loop" 124 N2 [label = 125 "vector.body:\l" + 126 " WIDEN-PHI ir\<%indvars.iv\> = phi ir\<0\>, ir\<%indvars.iv.next\>\l" + 127 " EMIT ir\<%arr.idx\> = getelementptr ir\<%A\>, ir\<%indvars.iv\>\l" + 128 " EMIT ir\<%l1\> = load ir\<%arr.idx\>\l" + 129 " EMIT ir\<%res\> = add ir\<%l1\>, ir\<10\>\l" + 130 " EMIT store ir\<%res\>, ir\<%arr.idx\>\l" + 131 " EMIT ir\<%indvars.iv.next\> = add ir\<%indvars.iv\>, ir\<1\>\l" + 132 " EMIT ir\<%exitcond\> = icmp ir\<%indvars.iv.next\>, ir\<%N\>\l" + 133 "Successor(s): vector.latch\l" 134 ] 135 N2 -> N4 [ label=""] 136 N4 [label = 137 "vector.latch:\l" + 138 "No successors\l" 139 ] 140 } 141 N4 -> N5 [ label="" ltail=cluster_N3] 142 N5 [label = 143 "middle.block:\l" + 144 " EMIT vp\<%cmp.n\> = icmp eq ir\<%N\>, vp\<%0\>\l" + 145 " EMIT branch-on-cond vp\<%cmp.n\>\l" + 146 "Successor(s): ir-bb\<for.end\>, scalar.ph\l" 147 ] 148 N5 -> N6 [ label="T"] 149 N5 -> N7 [ label="F"] 150 N6 [label = 151 "ir-bb\<for.end\>:\l" + 152 "No successors\l" 153 ] 154 N7 [label = 155 "scalar.ph:\l" + 156 "Successor(s): ir-bb\<for.body\>\l" 157 ] 158 N7 -> N8 [ label=""] 159 N8 [label = 160 "ir-bb\<for.body\>:\l" + 161 " IR %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\l" + 162 " IR %arr.idx = getelementptr inbounds i32, ptr %A, i64 %indvars.iv\l" + 163 " IR %l1 = load i32, ptr %arr.idx, align 4\l" + 164 " IR %res = add i32 %l1, 10\l" + 165 " IR store i32 %res, ptr %arr.idx, align 4\l" + 166 " IR %indvars.iv.next = add i64 %indvars.iv, 1\l" + 167 " IR %exitcond = icmp ne i64 %indvars.iv.next, %N\l" + 168 "No successors\l" 169 ] 170 } 171 )"; 172 EXPECT_EQ(ExpectedStr, FullDump); 173 #endif 174 TargetLibraryInfoImpl TLII(Triple(M.getTargetTriple())); 175 TargetLibraryInfo TLI(TLII); 176 VPlanTransforms::VPInstructionsToVPRecipes( 177 Plan, [](PHINode *P) { return nullptr; }, *SE, TLI); 178 } 179 180 TEST_F(VPlanHCFGTest, testVPInstructionToVPRecipesInner) { 181 const char *ModuleString = 182 "define void @f(ptr %A, i64 %N) {\n" 183 "entry:\n" 184 " br label %for.body\n" 185 "for.body:\n" 186 " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n" 187 " %arr.idx = getelementptr inbounds i32, ptr %A, i64 %indvars.iv\n" 188 " %l1 = load i32, ptr %arr.idx, align 4\n" 189 " %res = add i32 %l1, 10\n" 190 " store i32 %res, ptr %arr.idx, align 4\n" 191 " %indvars.iv.next = add i64 %indvars.iv, 1\n" 192 " %exitcond = icmp ne i64 %indvars.iv.next, %N\n" 193 " br i1 %exitcond, label %for.body, label %for.end\n" 194 "for.end:\n" 195 " ret void\n" 196 "}\n"; 197 198 Module &M = parseModule(ModuleString); 199 200 Function *F = M.getFunction("f"); 201 BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor(); 202 auto Plan = buildHCFG(LoopHeader); 203 204 TargetLibraryInfoImpl TLII(Triple(M.getTargetTriple())); 205 TargetLibraryInfo TLI(TLII); 206 VPlanTransforms::VPInstructionsToVPRecipes( 207 Plan, [](PHINode *P) { return nullptr; }, *SE, TLI); 208 209 VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock(); 210 EXPECT_NE(nullptr, Entry->getSingleSuccessor()); 211 EXPECT_EQ(0u, Entry->getNumPredecessors()); 212 EXPECT_EQ(1u, Entry->getNumSuccessors()); 213 214 // Check that the region following the preheader consists of a block for the 215 // original header and a separate latch. 216 VPBasicBlock *VecBB = Plan->getVectorLoopRegion()->getEntryBasicBlock(); 217 EXPECT_EQ(7u, VecBB->size()); 218 EXPECT_EQ(0u, VecBB->getNumPredecessors()); 219 EXPECT_EQ(1u, VecBB->getNumSuccessors()); 220 EXPECT_EQ(VecBB->getParent()->getEntryBasicBlock(), VecBB); 221 222 VPBlockBase *VecLatch = VecBB->getSingleSuccessor(); 223 EXPECT_EQ(VecLatch->getParent()->getExitingBasicBlock(), VecLatch); 224 EXPECT_EQ(0u, VecLatch->getNumSuccessors()); 225 226 auto Iter = VecBB->begin(); 227 EXPECT_NE(nullptr, dyn_cast<VPWidenPHIRecipe>(&*Iter++)); 228 EXPECT_NE(nullptr, dyn_cast<VPWidenGEPRecipe>(&*Iter++)); 229 EXPECT_NE(nullptr, dyn_cast<VPWidenMemoryRecipe>(&*Iter++)); 230 EXPECT_NE(nullptr, dyn_cast<VPWidenRecipe>(&*Iter++)); 231 EXPECT_NE(nullptr, dyn_cast<VPWidenMemoryRecipe>(&*Iter++)); 232 EXPECT_NE(nullptr, dyn_cast<VPWidenRecipe>(&*Iter++)); 233 EXPECT_NE(nullptr, dyn_cast<VPWidenRecipe>(&*Iter++)); 234 EXPECT_EQ(VecBB->end(), Iter); 235 } 236 237 TEST_F(VPlanHCFGTest, testBuildHCFGInnerLoopMultiExit) { 238 const char *ModuleString = 239 "define void @f(ptr %A, i64 %N) {\n" 240 "entry:\n" 241 " br label %loop.header\n" 242 "loop.header:\n" 243 " %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]\n" 244 " %arr.idx = getelementptr inbounds i32, ptr %A, i64 %iv\n" 245 " %l1 = load i32, ptr %arr.idx, align 4\n" 246 " %c = icmp eq i32 %l1, 0\n" 247 " br i1 %c, label %exit.1, label %loop.latch\n" 248 "loop.latch:\n" 249 " %res = add i32 %l1, 10\n" 250 " store i32 %res, ptr %arr.idx, align 4\n" 251 " %iv.next = add i64 %iv, 1\n" 252 " %exitcond = icmp ne i64 %iv.next, %N\n" 253 " br i1 %exitcond, label %loop.header, label %exit.2\n" 254 "exit.1:\n" 255 " ret void\n" 256 "exit.2:\n" 257 " ret void\n" 258 "}\n"; 259 260 Module &M = parseModule(ModuleString); 261 262 Function *F = M.getFunction("f"); 263 BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor(); 264 auto Plan = buildHCFG(LoopHeader); 265 266 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 267 // Add an external value to check we do not print the list of external values, 268 // as this is not required with the new printing. 269 Plan->getOrAddLiveIn(&*F->arg_begin()); 270 std::string FullDump; 271 raw_string_ostream OS(FullDump); 272 Plan->printDOT(OS); 273 const char *ExpectedStr = R"(digraph VPlan { 274 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"] 275 node [shape=rect, fontname=Courier, fontsize=30] 276 edge [fontname=Courier, fontsize=30] 277 compound=true 278 N0 [label = 279 "ir-bb\<entry\>:\l" + 280 "Successor(s): vector.ph\l" 281 ] 282 N0 -> N1 [ label=""] 283 N1 [label = 284 "vector.ph:\l" + 285 "Successor(s): vector loop\l" 286 ] 287 N1 -> N2 [ label="" lhead=cluster_N3] 288 subgraph cluster_N3 { 289 fontname=Courier 290 label="\<x1\> vector loop" 291 N2 [label = 292 "vector.body:\l" + 293 " WIDEN-PHI ir\<%iv\> = phi ir\<0\>, ir\<%iv.next\>\l" + 294 " EMIT ir\<%arr.idx\> = getelementptr ir\<%A\>, ir\<%iv\>\l" + 295 " EMIT ir\<%l1\> = load ir\<%arr.idx\>\l" + 296 " EMIT ir\<%c\> = icmp ir\<%l1\>, ir\<0\>\l" + 297 "Successor(s): loop.latch\l" 298 ] 299 N2 -> N4 [ label=""] 300 N4 [label = 301 "loop.latch:\l" + 302 " EMIT ir\<%res\> = add ir\<%l1\>, ir\<10\>\l" + 303 " EMIT store ir\<%res\>, ir\<%arr.idx\>\l" + 304 " EMIT ir\<%iv.next\> = add ir\<%iv\>, ir\<1\>\l" + 305 " EMIT ir\<%exitcond\> = icmp ir\<%iv.next\>, ir\<%N\>\l" + 306 "Successor(s): vector.latch\l" 307 ] 308 N4 -> N5 [ label=""] 309 N5 [label = 310 "vector.latch:\l" + 311 "No successors\l" 312 ] 313 } 314 N5 -> N6 [ label="" ltail=cluster_N3] 315 N6 [label = 316 "middle.block:\l" + 317 " EMIT vp\<%cmp.n\> = icmp eq ir\<%N\>, vp\<%0\>\l" + 318 " EMIT branch-on-cond vp\<%cmp.n\>\l" + 319 "Successor(s): ir-bb\<exit.2\>, scalar.ph\l" 320 ] 321 N6 -> N7 [ label="T"] 322 N6 -> N8 [ label="F"] 323 N7 [label = 324 "ir-bb\<exit.2\>:\l" + 325 "No successors\l" 326 ] 327 N8 [label = 328 "scalar.ph:\l" + 329 "Successor(s): ir-bb\<loop.header\>\l" 330 ] 331 N8 -> N9 [ label=""] 332 N9 [label = 333 "ir-bb\<loop.header\>:\l" + 334 " IR %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]\l" + 335 " IR %arr.idx = getelementptr inbounds i32, ptr %A, i64 %iv\l" + 336 " IR %l1 = load i32, ptr %arr.idx, align 4\l" + 337 " IR %c = icmp eq i32 %l1, 0\l" + 338 "No successors\l" 339 ] 340 } 341 )"; 342 EXPECT_EQ(ExpectedStr, FullDump); 343 #endif 344 } 345 346 } // namespace 347 } // namespace llvm 348