1 | //===- llvm/unittests/Transforms/Vectorize/VPlanTest.cpp - VPlan tests ----===// |
2 | // |
3 | // |
4 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
5 | // See https://llvm.org/LICENSE.txt for license information. |
6 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
7 | // |
8 | //===----------------------------------------------------------------------===// |
9 | |
10 | #include "../lib/Transforms/Vectorize/VPlan.h" |
11 | #include "../lib/Transforms/Vectorize/VPlanCFG.h" |
12 | #include "llvm/ADT/DepthFirstIterator.h" |
13 | #include "llvm/ADT/PostOrderIterator.h" |
14 | #include "llvm/Analysis/VectorUtils.h" |
15 | #include "llvm/IR/Instruction.h" |
16 | #include "llvm/IR/Instructions.h" |
17 | #include "gtest/gtest.h" |
18 | #include <string> |
19 | |
20 | namespace llvm { |
21 | namespace { |
22 | |
23 | #define CHECK_ITERATOR(Range1, ...) \ |
24 | do { \ |
25 | std::vector<VPInstruction *> Tmp = {__VA_ARGS__}; \ |
26 | EXPECT_EQ((size_t)std::distance(Range1.begin(), Range1.end()), \ |
27 | Tmp.size()); \ |
28 | for (auto Pair : zip(Range1, make_range(Tmp.begin(), Tmp.end()))) \ |
29 | EXPECT_EQ(&std::get<0>(Pair), std::get<1>(Pair)); \ |
30 | } while (0) |
31 | |
32 | TEST(VPInstructionTest, insertBefore) { |
33 | VPInstruction *I1 = new VPInstruction(0, {}); |
34 | VPInstruction *I2 = new VPInstruction(1, {}); |
35 | VPInstruction *I3 = new VPInstruction(2, {}); |
36 | |
37 | VPBasicBlock VPBB1; |
38 | VPBB1.appendRecipe(Recipe: I1); |
39 | |
40 | I2->insertBefore(InsertPos: I1); |
41 | CHECK_ITERATOR(VPBB1, I2, I1); |
42 | |
43 | I3->insertBefore(InsertPos: I2); |
44 | CHECK_ITERATOR(VPBB1, I3, I2, I1); |
45 | } |
46 | |
47 | TEST(VPInstructionTest, eraseFromParent) { |
48 | VPInstruction *I1 = new VPInstruction(0, {}); |
49 | VPInstruction *I2 = new VPInstruction(1, {}); |
50 | VPInstruction *I3 = new VPInstruction(2, {}); |
51 | |
52 | VPBasicBlock VPBB1; |
53 | VPBB1.appendRecipe(Recipe: I1); |
54 | VPBB1.appendRecipe(Recipe: I2); |
55 | VPBB1.appendRecipe(Recipe: I3); |
56 | |
57 | I2->eraseFromParent(); |
58 | CHECK_ITERATOR(VPBB1, I1, I3); |
59 | |
60 | I1->eraseFromParent(); |
61 | CHECK_ITERATOR(VPBB1, I3); |
62 | |
63 | I3->eraseFromParent(); |
64 | EXPECT_TRUE(VPBB1.empty()); |
65 | } |
66 | |
67 | TEST(VPInstructionTest, moveAfter) { |
68 | VPInstruction *I1 = new VPInstruction(0, {}); |
69 | VPInstruction *I2 = new VPInstruction(1, {}); |
70 | VPInstruction *I3 = new VPInstruction(2, {}); |
71 | |
72 | VPBasicBlock VPBB1; |
73 | VPBB1.appendRecipe(Recipe: I1); |
74 | VPBB1.appendRecipe(Recipe: I2); |
75 | VPBB1.appendRecipe(Recipe: I3); |
76 | |
77 | I1->moveAfter(MovePos: I2); |
78 | |
79 | CHECK_ITERATOR(VPBB1, I2, I1, I3); |
80 | |
81 | VPInstruction *I4 = new VPInstruction(4, {}); |
82 | VPInstruction *I5 = new VPInstruction(5, {}); |
83 | VPBasicBlock VPBB2; |
84 | VPBB2.appendRecipe(Recipe: I4); |
85 | VPBB2.appendRecipe(Recipe: I5); |
86 | |
87 | I3->moveAfter(MovePos: I4); |
88 | |
89 | CHECK_ITERATOR(VPBB1, I2, I1); |
90 | CHECK_ITERATOR(VPBB2, I4, I3, I5); |
91 | EXPECT_EQ(I3->getParent(), I4->getParent()); |
92 | } |
93 | |
94 | TEST(VPInstructionTest, moveBefore) { |
95 | VPInstruction *I1 = new VPInstruction(0, {}); |
96 | VPInstruction *I2 = new VPInstruction(1, {}); |
97 | VPInstruction *I3 = new VPInstruction(2, {}); |
98 | |
99 | VPBasicBlock VPBB1; |
100 | VPBB1.appendRecipe(Recipe: I1); |
101 | VPBB1.appendRecipe(Recipe: I2); |
102 | VPBB1.appendRecipe(Recipe: I3); |
103 | |
104 | I1->moveBefore(BB&: VPBB1, I: I3->getIterator()); |
105 | |
106 | CHECK_ITERATOR(VPBB1, I2, I1, I3); |
107 | |
108 | VPInstruction *I4 = new VPInstruction(4, {}); |
109 | VPInstruction *I5 = new VPInstruction(5, {}); |
110 | VPBasicBlock VPBB2; |
111 | VPBB2.appendRecipe(Recipe: I4); |
112 | VPBB2.appendRecipe(Recipe: I5); |
113 | |
114 | I3->moveBefore(BB&: VPBB2, I: I4->getIterator()); |
115 | |
116 | CHECK_ITERATOR(VPBB1, I2, I1); |
117 | CHECK_ITERATOR(VPBB2, I3, I4, I5); |
118 | EXPECT_EQ(I3->getParent(), I4->getParent()); |
119 | |
120 | VPBasicBlock VPBB3; |
121 | |
122 | I4->moveBefore(BB&: VPBB3, I: VPBB3.end()); |
123 | |
124 | CHECK_ITERATOR(VPBB1, I2, I1); |
125 | CHECK_ITERATOR(VPBB2, I3, I5); |
126 | CHECK_ITERATOR(VPBB3, I4); |
127 | EXPECT_EQ(&VPBB3, I4->getParent()); |
128 | } |
129 | |
130 | TEST(VPInstructionTest, setOperand) { |
131 | VPValue *VPV1 = new VPValue(); |
132 | VPValue *VPV2 = new VPValue(); |
133 | VPInstruction *I1 = new VPInstruction(0, {VPV1, VPV2}); |
134 | EXPECT_EQ(1u, VPV1->getNumUsers()); |
135 | EXPECT_EQ(I1, *VPV1->user_begin()); |
136 | EXPECT_EQ(1u, VPV2->getNumUsers()); |
137 | EXPECT_EQ(I1, *VPV2->user_begin()); |
138 | |
139 | // Replace operand 0 (VPV1) with VPV3. |
140 | VPValue *VPV3 = new VPValue(); |
141 | I1->setOperand(I: 0, New: VPV3); |
142 | EXPECT_EQ(0u, VPV1->getNumUsers()); |
143 | EXPECT_EQ(1u, VPV2->getNumUsers()); |
144 | EXPECT_EQ(I1, *VPV2->user_begin()); |
145 | EXPECT_EQ(1u, VPV3->getNumUsers()); |
146 | EXPECT_EQ(I1, *VPV3->user_begin()); |
147 | |
148 | // Replace operand 1 (VPV2) with VPV3. |
149 | I1->setOperand(I: 1, New: VPV3); |
150 | EXPECT_EQ(0u, VPV1->getNumUsers()); |
151 | EXPECT_EQ(0u, VPV2->getNumUsers()); |
152 | EXPECT_EQ(2u, VPV3->getNumUsers()); |
153 | EXPECT_EQ(I1, *VPV3->user_begin()); |
154 | EXPECT_EQ(I1, *std::next(VPV3->user_begin())); |
155 | |
156 | // Replace operand 0 (VPV3) with VPV4. |
157 | VPValue *VPV4 = new VPValue(); |
158 | I1->setOperand(I: 0, New: VPV4); |
159 | EXPECT_EQ(1u, VPV3->getNumUsers()); |
160 | EXPECT_EQ(I1, *VPV3->user_begin()); |
161 | EXPECT_EQ(I1, *VPV4->user_begin()); |
162 | |
163 | // Replace operand 1 (VPV3) with VPV4. |
164 | I1->setOperand(I: 1, New: VPV4); |
165 | EXPECT_EQ(0u, VPV3->getNumUsers()); |
166 | EXPECT_EQ(I1, *VPV4->user_begin()); |
167 | EXPECT_EQ(I1, *std::next(VPV4->user_begin())); |
168 | |
169 | delete I1; |
170 | delete VPV1; |
171 | delete VPV2; |
172 | delete VPV3; |
173 | delete VPV4; |
174 | } |
175 | |
176 | TEST(VPInstructionTest, replaceAllUsesWith) { |
177 | VPValue *VPV1 = new VPValue(); |
178 | VPValue *VPV2 = new VPValue(); |
179 | VPInstruction *I1 = new VPInstruction(0, {VPV1, VPV2}); |
180 | |
181 | // Replace all uses of VPV1 with VPV3. |
182 | VPValue *VPV3 = new VPValue(); |
183 | VPV1->replaceAllUsesWith(New: VPV3); |
184 | EXPECT_EQ(VPV3, I1->getOperand(0)); |
185 | EXPECT_EQ(VPV2, I1->getOperand(1)); |
186 | EXPECT_EQ(0u, VPV1->getNumUsers()); |
187 | EXPECT_EQ(1u, VPV2->getNumUsers()); |
188 | EXPECT_EQ(I1, *VPV2->user_begin()); |
189 | EXPECT_EQ(1u, VPV3->getNumUsers()); |
190 | EXPECT_EQ(I1, *VPV3->user_begin()); |
191 | |
192 | // Replace all uses of VPV2 with VPV3. |
193 | VPV2->replaceAllUsesWith(New: VPV3); |
194 | EXPECT_EQ(VPV3, I1->getOperand(0)); |
195 | EXPECT_EQ(VPV3, I1->getOperand(1)); |
196 | EXPECT_EQ(0u, VPV1->getNumUsers()); |
197 | EXPECT_EQ(0u, VPV2->getNumUsers()); |
198 | EXPECT_EQ(2u, VPV3->getNumUsers()); |
199 | EXPECT_EQ(I1, *VPV3->user_begin()); |
200 | |
201 | // Replace all uses of VPV3 with VPV1. |
202 | VPV3->replaceAllUsesWith(New: VPV1); |
203 | EXPECT_EQ(VPV1, I1->getOperand(0)); |
204 | EXPECT_EQ(VPV1, I1->getOperand(1)); |
205 | EXPECT_EQ(2u, VPV1->getNumUsers()); |
206 | EXPECT_EQ(I1, *VPV1->user_begin()); |
207 | EXPECT_EQ(0u, VPV2->getNumUsers()); |
208 | EXPECT_EQ(0u, VPV3->getNumUsers()); |
209 | |
210 | VPInstruction *I2 = new VPInstruction(0, {VPV1, VPV2}); |
211 | EXPECT_EQ(3u, VPV1->getNumUsers()); |
212 | VPV1->replaceAllUsesWith(New: VPV3); |
213 | EXPECT_EQ(3u, VPV3->getNumUsers()); |
214 | |
215 | delete I1; |
216 | delete I2; |
217 | delete VPV1; |
218 | delete VPV2; |
219 | delete VPV3; |
220 | } |
221 | |
222 | TEST(VPInstructionTest, releaseOperandsAtDeletion) { |
223 | VPValue *VPV1 = new VPValue(); |
224 | VPValue *VPV2 = new VPValue(); |
225 | VPInstruction *I1 = new VPInstruction(0, {VPV1, VPV2}); |
226 | |
227 | EXPECT_EQ(1u, VPV1->getNumUsers()); |
228 | EXPECT_EQ(I1, *VPV1->user_begin()); |
229 | EXPECT_EQ(1u, VPV2->getNumUsers()); |
230 | EXPECT_EQ(I1, *VPV2->user_begin()); |
231 | |
232 | delete I1; |
233 | |
234 | EXPECT_EQ(0u, VPV1->getNumUsers()); |
235 | EXPECT_EQ(0u, VPV2->getNumUsers()); |
236 | |
237 | delete VPV1; |
238 | delete VPV2; |
239 | } |
240 | TEST(VPBasicBlockTest, getPlan) { |
241 | { |
242 | VPBasicBlock *VPPH = new VPBasicBlock("ph" ); |
243 | VPBasicBlock *VPBB1 = new VPBasicBlock(); |
244 | VPBasicBlock *VPBB2 = new VPBasicBlock(); |
245 | VPBasicBlock *VPBB3 = new VPBasicBlock(); |
246 | VPBasicBlock *VPBB4 = new VPBasicBlock(); |
247 | |
248 | // VPBB1 |
249 | // / \ |
250 | // VPBB2 VPBB3 |
251 | // \ / |
252 | // VPBB4 |
253 | VPBlockUtils::connectBlocks(From: VPBB1, To: VPBB2); |
254 | VPBlockUtils::connectBlocks(From: VPBB1, To: VPBB3); |
255 | VPBlockUtils::connectBlocks(From: VPBB2, To: VPBB4); |
256 | VPBlockUtils::connectBlocks(From: VPBB3, To: VPBB4); |
257 | |
258 | auto TC = std::make_unique<VPValue>(); |
259 | VPlan Plan(VPPH, &*TC, VPBB1); |
260 | |
261 | EXPECT_EQ(&Plan, VPBB1->getPlan()); |
262 | EXPECT_EQ(&Plan, VPBB2->getPlan()); |
263 | EXPECT_EQ(&Plan, VPBB3->getPlan()); |
264 | EXPECT_EQ(&Plan, VPBB4->getPlan()); |
265 | } |
266 | |
267 | { |
268 | VPBasicBlock *VPPH = new VPBasicBlock("ph" ); |
269 | // VPBasicBlock is the entry into the VPlan, followed by a region. |
270 | VPBasicBlock *R1BB1 = new VPBasicBlock(); |
271 | VPBasicBlock *R1BB2 = new VPBasicBlock(); |
272 | VPRegionBlock *R1 = new VPRegionBlock(R1BB1, R1BB2, "R1" ); |
273 | VPBlockUtils::connectBlocks(From: R1BB1, To: R1BB2); |
274 | |
275 | VPBasicBlock *VPBB1 = new VPBasicBlock(); |
276 | VPBlockUtils::connectBlocks(From: VPBB1, To: R1); |
277 | |
278 | auto TC = std::make_unique<VPValue>(); |
279 | VPlan Plan(VPPH, &*TC, VPBB1); |
280 | |
281 | EXPECT_EQ(&Plan, VPBB1->getPlan()); |
282 | EXPECT_EQ(&Plan, R1->getPlan()); |
283 | EXPECT_EQ(&Plan, R1BB1->getPlan()); |
284 | EXPECT_EQ(&Plan, R1BB2->getPlan()); |
285 | } |
286 | |
287 | { |
288 | VPBasicBlock *VPPH = new VPBasicBlock("ph" ); |
289 | |
290 | VPBasicBlock *R1BB1 = new VPBasicBlock(); |
291 | VPBasicBlock *R1BB2 = new VPBasicBlock(); |
292 | VPRegionBlock *R1 = new VPRegionBlock(R1BB1, R1BB2, "R1" ); |
293 | VPBlockUtils::connectBlocks(From: R1BB1, To: R1BB2); |
294 | |
295 | VPBasicBlock *R2BB1 = new VPBasicBlock(); |
296 | VPBasicBlock *R2BB2 = new VPBasicBlock(); |
297 | VPRegionBlock *R2 = new VPRegionBlock(R2BB1, R2BB2, "R2" ); |
298 | VPBlockUtils::connectBlocks(From: R2BB1, To: R2BB2); |
299 | |
300 | VPBasicBlock *VPBB1 = new VPBasicBlock(); |
301 | VPBlockUtils::connectBlocks(From: VPBB1, To: R1); |
302 | VPBlockUtils::connectBlocks(From: VPBB1, To: R2); |
303 | |
304 | VPBasicBlock *VPBB2 = new VPBasicBlock(); |
305 | VPBlockUtils::connectBlocks(From: R1, To: VPBB2); |
306 | VPBlockUtils::connectBlocks(From: R2, To: VPBB2); |
307 | |
308 | auto TC = std::make_unique<VPValue>(); |
309 | VPlan Plan(VPPH, &*TC, VPBB1); |
310 | |
311 | EXPECT_EQ(&Plan, VPBB1->getPlan()); |
312 | EXPECT_EQ(&Plan, R1->getPlan()); |
313 | EXPECT_EQ(&Plan, R1BB1->getPlan()); |
314 | EXPECT_EQ(&Plan, R1BB2->getPlan()); |
315 | EXPECT_EQ(&Plan, R2->getPlan()); |
316 | EXPECT_EQ(&Plan, R2BB1->getPlan()); |
317 | EXPECT_EQ(&Plan, R2BB2->getPlan()); |
318 | EXPECT_EQ(&Plan, VPBB2->getPlan()); |
319 | } |
320 | } |
321 | |
322 | TEST(VPBasicBlockTest, TraversingIteratorTest) { |
323 | { |
324 | // VPBasicBlocks only |
325 | // VPBB1 |
326 | // / \ |
327 | // VPBB2 VPBB3 |
328 | // \ / |
329 | // VPBB4 |
330 | // |
331 | VPBasicBlock *VPPH = new VPBasicBlock("ph" ); |
332 | VPBasicBlock *VPBB1 = new VPBasicBlock(); |
333 | VPBasicBlock *VPBB2 = new VPBasicBlock(); |
334 | VPBasicBlock *VPBB3 = new VPBasicBlock(); |
335 | VPBasicBlock *VPBB4 = new VPBasicBlock(); |
336 | |
337 | VPBlockUtils::connectBlocks(From: VPBB1, To: VPBB2); |
338 | VPBlockUtils::connectBlocks(From: VPBB1, To: VPBB3); |
339 | VPBlockUtils::connectBlocks(From: VPBB2, To: VPBB4); |
340 | VPBlockUtils::connectBlocks(From: VPBB3, To: VPBB4); |
341 | |
342 | VPBlockDeepTraversalWrapper<const VPBlockBase *> Start(VPBB1); |
343 | SmallVector<const VPBlockBase *> FromIterator(depth_first(G: Start)); |
344 | EXPECT_EQ(4u, FromIterator.size()); |
345 | EXPECT_EQ(VPBB1, FromIterator[0]); |
346 | EXPECT_EQ(VPBB2, FromIterator[1]); |
347 | |
348 | // Use Plan to properly clean up created blocks. |
349 | auto TC = std::make_unique<VPValue>(); |
350 | VPlan Plan(VPPH, &*TC, VPBB1); |
351 | } |
352 | |
353 | { |
354 | // 2 consecutive regions. |
355 | // VPBB0 |
356 | // | |
357 | // R1 { |
358 | // \ |
359 | // R1BB1 |
360 | // / \ |--| |
361 | // R1BB2 R1BB3 -| |
362 | // \ / |
363 | // R1BB4 |
364 | // } |
365 | // | |
366 | // R2 { |
367 | // \ |
368 | // R2BB1 |
369 | // | |
370 | // R2BB2 |
371 | // |
372 | VPBasicBlock *VPPH = new VPBasicBlock("ph" ); |
373 | VPBasicBlock *VPBB0 = new VPBasicBlock("VPBB0" ); |
374 | VPBasicBlock *R1BB1 = new VPBasicBlock(); |
375 | VPBasicBlock *R1BB2 = new VPBasicBlock(); |
376 | VPBasicBlock *R1BB3 = new VPBasicBlock(); |
377 | VPBasicBlock *R1BB4 = new VPBasicBlock(); |
378 | VPRegionBlock *R1 = new VPRegionBlock(R1BB1, R1BB4, "R1" ); |
379 | R1BB2->setParent(R1); |
380 | R1BB3->setParent(R1); |
381 | VPBlockUtils::connectBlocks(From: VPBB0, To: R1); |
382 | VPBlockUtils::connectBlocks(From: R1BB1, To: R1BB2); |
383 | VPBlockUtils::connectBlocks(From: R1BB1, To: R1BB3); |
384 | VPBlockUtils::connectBlocks(From: R1BB2, To: R1BB4); |
385 | VPBlockUtils::connectBlocks(From: R1BB3, To: R1BB4); |
386 | // Cycle. |
387 | VPBlockUtils::connectBlocks(From: R1BB3, To: R1BB3); |
388 | |
389 | VPBasicBlock *R2BB1 = new VPBasicBlock(); |
390 | VPBasicBlock *R2BB2 = new VPBasicBlock(); |
391 | VPRegionBlock *R2 = new VPRegionBlock(R2BB1, R2BB2, "R2" ); |
392 | VPBlockUtils::connectBlocks(From: R2BB1, To: R2BB2); |
393 | VPBlockUtils::connectBlocks(From: R1, To: R2); |
394 | |
395 | // Successors of R1. |
396 | SmallVector<const VPBlockBase *> FromIterator( |
397 | VPAllSuccessorsIterator<VPBlockBase *>(R1), |
398 | VPAllSuccessorsIterator<VPBlockBase *>::end(Block: R1)); |
399 | EXPECT_EQ(1u, FromIterator.size()); |
400 | EXPECT_EQ(R1BB1, FromIterator[0]); |
401 | |
402 | // Depth-first. |
403 | VPBlockDeepTraversalWrapper<VPBlockBase *> Start(R1); |
404 | FromIterator.clear(); |
405 | copy(first: df_begin(G: Start), last: df_end(G: Start), result: std::back_inserter(x&: FromIterator)); |
406 | EXPECT_EQ(8u, FromIterator.size()); |
407 | EXPECT_EQ(R1, FromIterator[0]); |
408 | EXPECT_EQ(R1BB1, FromIterator[1]); |
409 | EXPECT_EQ(R1BB2, FromIterator[2]); |
410 | EXPECT_EQ(R1BB4, FromIterator[3]); |
411 | EXPECT_EQ(R2, FromIterator[4]); |
412 | EXPECT_EQ(R2BB1, FromIterator[5]); |
413 | EXPECT_EQ(R2BB2, FromIterator[6]); |
414 | EXPECT_EQ(R1BB3, FromIterator[7]); |
415 | |
416 | // const VPBasicBlocks only. |
417 | FromIterator.clear(); |
418 | copy(Range: VPBlockUtils::blocksOnly<const VPBasicBlock>(Range: depth_first(G: Start)), |
419 | Out: std::back_inserter(x&: FromIterator)); |
420 | EXPECT_EQ(6u, FromIterator.size()); |
421 | EXPECT_EQ(R1BB1, FromIterator[0]); |
422 | EXPECT_EQ(R1BB2, FromIterator[1]); |
423 | EXPECT_EQ(R1BB4, FromIterator[2]); |
424 | EXPECT_EQ(R2BB1, FromIterator[3]); |
425 | EXPECT_EQ(R2BB2, FromIterator[4]); |
426 | EXPECT_EQ(R1BB3, FromIterator[5]); |
427 | |
428 | // VPRegionBlocks only. |
429 | SmallVector<VPRegionBlock *> FromIteratorVPRegion( |
430 | VPBlockUtils::blocksOnly<VPRegionBlock>(Range: depth_first(G: Start))); |
431 | EXPECT_EQ(2u, FromIteratorVPRegion.size()); |
432 | EXPECT_EQ(R1, FromIteratorVPRegion[0]); |
433 | EXPECT_EQ(R2, FromIteratorVPRegion[1]); |
434 | |
435 | // Post-order. |
436 | FromIterator.clear(); |
437 | copy(Range: post_order(G: Start), Out: std::back_inserter(x&: FromIterator)); |
438 | EXPECT_EQ(8u, FromIterator.size()); |
439 | EXPECT_EQ(R2BB2, FromIterator[0]); |
440 | EXPECT_EQ(R2BB1, FromIterator[1]); |
441 | EXPECT_EQ(R2, FromIterator[2]); |
442 | EXPECT_EQ(R1BB4, FromIterator[3]); |
443 | EXPECT_EQ(R1BB2, FromIterator[4]); |
444 | EXPECT_EQ(R1BB3, FromIterator[5]); |
445 | EXPECT_EQ(R1BB1, FromIterator[6]); |
446 | EXPECT_EQ(R1, FromIterator[7]); |
447 | |
448 | // Use Plan to properly clean up created blocks. |
449 | auto TC = std::make_unique<VPValue>(); |
450 | VPlan Plan(VPPH, &*TC, VPBB0); |
451 | } |
452 | |
453 | { |
454 | // 2 nested regions. |
455 | // VPBB1 |
456 | // | |
457 | // R1 { |
458 | // R1BB1 |
459 | // / \ |
460 | // R2 { | |
461 | // \ | |
462 | // R2BB1 | |
463 | // | \ R1BB2 |
464 | // R2BB2-| | |
465 | // \ | |
466 | // R2BB3 | |
467 | // } / |
468 | // \ / |
469 | // R1BB3 |
470 | // } |
471 | // | |
472 | // VPBB2 |
473 | // |
474 | VPBasicBlock *VPPH = new VPBasicBlock("ph" ); |
475 | VPBasicBlock *R1BB1 = new VPBasicBlock("R1BB1" ); |
476 | VPBasicBlock *R1BB2 = new VPBasicBlock("R1BB2" ); |
477 | VPBasicBlock *R1BB3 = new VPBasicBlock("R1BB3" ); |
478 | VPRegionBlock *R1 = new VPRegionBlock(R1BB1, R1BB3, "R1" ); |
479 | |
480 | VPBasicBlock *R2BB1 = new VPBasicBlock("R2BB1" ); |
481 | VPBasicBlock *R2BB2 = new VPBasicBlock("R2BB2" ); |
482 | VPBasicBlock *R2BB3 = new VPBasicBlock("R2BB3" ); |
483 | VPRegionBlock *R2 = new VPRegionBlock(R2BB1, R2BB3, "R2" ); |
484 | R2BB2->setParent(R2); |
485 | VPBlockUtils::connectBlocks(From: R2BB1, To: R2BB2); |
486 | VPBlockUtils::connectBlocks(From: R2BB2, To: R2BB1); |
487 | VPBlockUtils::connectBlocks(From: R2BB2, To: R2BB3); |
488 | |
489 | R2->setParent(R1); |
490 | VPBlockUtils::connectBlocks(From: R1BB1, To: R2); |
491 | R1BB2->setParent(R1); |
492 | VPBlockUtils::connectBlocks(From: R1BB1, To: R1BB2); |
493 | VPBlockUtils::connectBlocks(From: R1BB2, To: R1BB3); |
494 | VPBlockUtils::connectBlocks(From: R2, To: R1BB3); |
495 | |
496 | VPBasicBlock *VPBB1 = new VPBasicBlock("VPBB1" ); |
497 | VPBlockUtils::connectBlocks(From: VPBB1, To: R1); |
498 | VPBasicBlock *VPBB2 = new VPBasicBlock("VPBB2" ); |
499 | VPBlockUtils::connectBlocks(From: R1, To: VPBB2); |
500 | |
501 | // Depth-first. |
502 | VPBlockDeepTraversalWrapper<VPBlockBase *> Start(VPBB1); |
503 | SmallVector<VPBlockBase *> FromIterator(depth_first(G: Start)); |
504 | EXPECT_EQ(10u, FromIterator.size()); |
505 | EXPECT_EQ(VPBB1, FromIterator[0]); |
506 | EXPECT_EQ(R1, FromIterator[1]); |
507 | EXPECT_EQ(R1BB1, FromIterator[2]); |
508 | EXPECT_EQ(R2, FromIterator[3]); |
509 | EXPECT_EQ(R2BB1, FromIterator[4]); |
510 | EXPECT_EQ(R2BB2, FromIterator[5]); |
511 | EXPECT_EQ(R2BB3, FromIterator[6]); |
512 | EXPECT_EQ(R1BB3, FromIterator[7]); |
513 | EXPECT_EQ(VPBB2, FromIterator[8]); |
514 | EXPECT_EQ(R1BB2, FromIterator[9]); |
515 | |
516 | // Post-order. |
517 | FromIterator.clear(); |
518 | FromIterator.append(in_start: po_begin(G: Start), in_end: po_end(G: Start)); |
519 | EXPECT_EQ(10u, FromIterator.size()); |
520 | EXPECT_EQ(VPBB2, FromIterator[0]); |
521 | EXPECT_EQ(R1BB3, FromIterator[1]); |
522 | EXPECT_EQ(R2BB3, FromIterator[2]); |
523 | EXPECT_EQ(R2BB2, FromIterator[3]); |
524 | EXPECT_EQ(R2BB1, FromIterator[4]); |
525 | EXPECT_EQ(R2, FromIterator[5]); |
526 | EXPECT_EQ(R1BB2, FromIterator[6]); |
527 | EXPECT_EQ(R1BB1, FromIterator[7]); |
528 | EXPECT_EQ(R1, FromIterator[8]); |
529 | EXPECT_EQ(VPBB1, FromIterator[9]); |
530 | |
531 | // Use Plan to properly clean up created blocks. |
532 | auto TC = std::make_unique<VPValue>(); |
533 | VPlan Plan(VPPH, &*TC, VPBB1); |
534 | } |
535 | |
536 | { |
537 | // VPBB1 |
538 | // | |
539 | // R1 { |
540 | // \ |
541 | // R2 { |
542 | // R2BB1 |
543 | // | |
544 | // R2BB2 |
545 | // } |
546 | // |
547 | VPBasicBlock *VPPH = new VPBasicBlock("ph" ); |
548 | VPBasicBlock *R2BB1 = new VPBasicBlock("R2BB1" ); |
549 | VPBasicBlock *R2BB2 = new VPBasicBlock("R2BB2" ); |
550 | VPRegionBlock *R2 = new VPRegionBlock(R2BB1, R2BB2, "R2" ); |
551 | VPBlockUtils::connectBlocks(From: R2BB1, To: R2BB2); |
552 | |
553 | VPRegionBlock *R1 = new VPRegionBlock(R2, R2, "R1" ); |
554 | R2->setParent(R1); |
555 | |
556 | VPBasicBlock *VPBB1 = new VPBasicBlock("VPBB1" ); |
557 | VPBlockUtils::connectBlocks(From: VPBB1, To: R1); |
558 | |
559 | // Depth-first. |
560 | VPBlockDeepTraversalWrapper<VPBlockBase *> Start(VPBB1); |
561 | SmallVector<VPBlockBase *> FromIterator(depth_first(G: Start)); |
562 | EXPECT_EQ(5u, FromIterator.size()); |
563 | EXPECT_EQ(VPBB1, FromIterator[0]); |
564 | EXPECT_EQ(R1, FromIterator[1]); |
565 | EXPECT_EQ(R2, FromIterator[2]); |
566 | EXPECT_EQ(R2BB1, FromIterator[3]); |
567 | EXPECT_EQ(R2BB2, FromIterator[4]); |
568 | |
569 | // Post-order. |
570 | FromIterator.clear(); |
571 | FromIterator.append(in_start: po_begin(G: Start), in_end: po_end(G: Start)); |
572 | EXPECT_EQ(5u, FromIterator.size()); |
573 | EXPECT_EQ(R2BB2, FromIterator[0]); |
574 | EXPECT_EQ(R2BB1, FromIterator[1]); |
575 | EXPECT_EQ(R2, FromIterator[2]); |
576 | EXPECT_EQ(R1, FromIterator[3]); |
577 | EXPECT_EQ(VPBB1, FromIterator[4]); |
578 | |
579 | // Use Plan to properly clean up created blocks. |
580 | auto TC = std::make_unique<VPValue>(); |
581 | VPlan Plan(VPPH, &*TC, VPBB1); |
582 | } |
583 | |
584 | { |
585 | // Nested regions with both R3 and R2 being exit nodes without successors. |
586 | // The successors of R1 should be used. |
587 | // |
588 | // VPBB1 |
589 | // | |
590 | // R1 { |
591 | // \ |
592 | // R2 { |
593 | // \ |
594 | // R2BB1 |
595 | // | |
596 | // R3 { |
597 | // R3BB1 |
598 | // } |
599 | // } |
600 | // | |
601 | // VPBB2 |
602 | // |
603 | VPBasicBlock *VPPH = new VPBasicBlock("ph" ); |
604 | VPBasicBlock *R3BB1 = new VPBasicBlock("R3BB1" ); |
605 | VPRegionBlock *R3 = new VPRegionBlock(R3BB1, R3BB1, "R3" ); |
606 | |
607 | VPBasicBlock *R2BB1 = new VPBasicBlock("R2BB1" ); |
608 | VPRegionBlock *R2 = new VPRegionBlock(R2BB1, R3, "R2" ); |
609 | R3->setParent(R2); |
610 | VPBlockUtils::connectBlocks(From: R2BB1, To: R3); |
611 | |
612 | VPRegionBlock *R1 = new VPRegionBlock(R2, R2, "R1" ); |
613 | R2->setParent(R1); |
614 | |
615 | VPBasicBlock *VPBB1 = new VPBasicBlock("VPBB1" ); |
616 | VPBasicBlock *VPBB2 = new VPBasicBlock("VPBB2" ); |
617 | VPBlockUtils::connectBlocks(From: VPBB1, To: R1); |
618 | VPBlockUtils::connectBlocks(From: R1, To: VPBB2); |
619 | |
620 | // Depth-first. |
621 | VPBlockDeepTraversalWrapper<VPBlockBase *> Start(VPBB1); |
622 | SmallVector<VPBlockBase *> FromIterator(depth_first(G: Start)); |
623 | EXPECT_EQ(7u, FromIterator.size()); |
624 | EXPECT_EQ(VPBB1, FromIterator[0]); |
625 | EXPECT_EQ(R1, FromIterator[1]); |
626 | EXPECT_EQ(R2, FromIterator[2]); |
627 | EXPECT_EQ(R2BB1, FromIterator[3]); |
628 | EXPECT_EQ(R3, FromIterator[4]); |
629 | EXPECT_EQ(R3BB1, FromIterator[5]); |
630 | EXPECT_EQ(VPBB2, FromIterator[6]); |
631 | |
632 | SmallVector<VPBlockBase *> FromIteratorVPBB; |
633 | copy(Range: VPBlockUtils::blocksOnly<VPBasicBlock>(Range: depth_first(G: Start)), |
634 | Out: std::back_inserter(x&: FromIteratorVPBB)); |
635 | EXPECT_EQ(VPBB1, FromIteratorVPBB[0]); |
636 | EXPECT_EQ(R2BB1, FromIteratorVPBB[1]); |
637 | EXPECT_EQ(R3BB1, FromIteratorVPBB[2]); |
638 | EXPECT_EQ(VPBB2, FromIteratorVPBB[3]); |
639 | |
640 | // Post-order. |
641 | FromIterator.clear(); |
642 | copy(Range: post_order(G: Start), Out: std::back_inserter(x&: FromIterator)); |
643 | EXPECT_EQ(7u, FromIterator.size()); |
644 | EXPECT_EQ(VPBB2, FromIterator[0]); |
645 | EXPECT_EQ(R3BB1, FromIterator[1]); |
646 | EXPECT_EQ(R3, FromIterator[2]); |
647 | EXPECT_EQ(R2BB1, FromIterator[3]); |
648 | EXPECT_EQ(R2, FromIterator[4]); |
649 | EXPECT_EQ(R1, FromIterator[5]); |
650 | EXPECT_EQ(VPBB1, FromIterator[6]); |
651 | |
652 | // Post-order, const VPRegionBlocks only. |
653 | VPBlockDeepTraversalWrapper<const VPBlockBase *> StartConst(VPBB1); |
654 | SmallVector<const VPRegionBlock *> FromIteratorVPRegion( |
655 | VPBlockUtils::blocksOnly<const VPRegionBlock>(Range: post_order(G: StartConst))); |
656 | EXPECT_EQ(3u, FromIteratorVPRegion.size()); |
657 | EXPECT_EQ(R3, FromIteratorVPRegion[0]); |
658 | EXPECT_EQ(R2, FromIteratorVPRegion[1]); |
659 | EXPECT_EQ(R1, FromIteratorVPRegion[2]); |
660 | |
661 | // Post-order, VPBasicBlocks only. |
662 | FromIterator.clear(); |
663 | copy(Range: VPBlockUtils::blocksOnly<VPBasicBlock>(Range: post_order(G: Start)), |
664 | Out: std::back_inserter(x&: FromIterator)); |
665 | EXPECT_EQ(FromIterator.size(), 4u); |
666 | EXPECT_EQ(VPBB2, FromIterator[0]); |
667 | EXPECT_EQ(R3BB1, FromIterator[1]); |
668 | EXPECT_EQ(R2BB1, FromIterator[2]); |
669 | EXPECT_EQ(VPBB1, FromIterator[3]); |
670 | |
671 | // Use Plan to properly clean up created blocks. |
672 | auto TC = std::make_unique<VPValue>(); |
673 | VPlan Plan(VPPH, &*TC, VPBB1); |
674 | } |
675 | } |
676 | |
677 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
678 | TEST(VPBasicBlockTest, print) { |
679 | VPInstruction *TC = new VPInstruction(Instruction::Add, {}); |
680 | VPBasicBlock *VPBB0 = new VPBasicBlock("preheader" ); |
681 | VPBB0->appendRecipe(Recipe: TC); |
682 | |
683 | VPInstruction *I1 = new VPInstruction(Instruction::Add, {}); |
684 | VPInstruction *I2 = new VPInstruction(Instruction::Sub, {I1}); |
685 | VPInstruction *I3 = new VPInstruction(Instruction::Br, {I1, I2}); |
686 | |
687 | VPBasicBlock *VPBB1 = new VPBasicBlock(); |
688 | VPBB1->appendRecipe(Recipe: I1); |
689 | VPBB1->appendRecipe(Recipe: I2); |
690 | VPBB1->appendRecipe(Recipe: I3); |
691 | VPBB1->setName("bb1" ); |
692 | |
693 | VPInstruction *I4 = new VPInstruction(Instruction::Mul, {I2, I1}); |
694 | VPInstruction *I5 = new VPInstruction(Instruction::Ret, {I4}); |
695 | VPBasicBlock *VPBB2 = new VPBasicBlock(); |
696 | VPBB2->appendRecipe(Recipe: I4); |
697 | VPBB2->appendRecipe(Recipe: I5); |
698 | VPBB2->setName("bb2" ); |
699 | |
700 | VPBlockUtils::connectBlocks(From: VPBB1, To: VPBB2); |
701 | |
702 | // Check printing an instruction without associated VPlan. |
703 | { |
704 | std::string I3Dump; |
705 | raw_string_ostream OS(I3Dump); |
706 | VPSlotTracker SlotTracker; |
707 | I3->print(O&: OS, Indent: "" , SlotTracker); |
708 | OS.flush(); |
709 | EXPECT_EQ("EMIT br <badref>, <badref>" , I3Dump); |
710 | } |
711 | |
712 | VPlan Plan(VPBB0, TC, VPBB1); |
713 | std::string FullDump; |
714 | raw_string_ostream OS(FullDump); |
715 | Plan.printDOT(O&: OS); |
716 | |
717 | const char *ExpectedStr = R"(digraph VPlan { |
718 | graph [labelloc=t, fontsize=30; label="Vectorization Plan\n for UF\>=1\nvp\<%1\> = original trip-count\n"] |
719 | node [shape=rect, fontname=Courier, fontsize=30] |
720 | edge [fontname=Courier, fontsize=30] |
721 | compound=true |
722 | N0 [label = |
723 | "preheader:\l" + |
724 | " EMIT vp\<%1\> = add\l" + |
725 | "No successors\l" |
726 | ] |
727 | N1 [label = |
728 | "bb1:\l" + |
729 | " EMIT vp\<%2\> = add\l" + |
730 | " EMIT vp\<%3\> = sub vp\<%2\>\l" + |
731 | " EMIT br vp\<%2\>, vp\<%3\>\l" + |
732 | "Successor(s): bb2\l" |
733 | ] |
734 | N1 -> N2 [ label=""] |
735 | N2 [label = |
736 | "bb2:\l" + |
737 | " EMIT vp\<%5\> = mul vp\<%3\>, vp\<%2\>\l" + |
738 | " EMIT ret vp\<%5\>\l" + |
739 | "No successors\l" |
740 | ] |
741 | } |
742 | )" ; |
743 | EXPECT_EQ(ExpectedStr, FullDump); |
744 | |
745 | const char *ExpectedBlock1Str = R"(bb1: |
746 | EMIT vp<%2> = add |
747 | EMIT vp<%3> = sub vp<%2> |
748 | EMIT br vp<%2>, vp<%3> |
749 | Successor(s): bb2 |
750 | )" ; |
751 | std::string Block1Dump; |
752 | raw_string_ostream OS1(Block1Dump); |
753 | VPBB1->print(O&: OS1); |
754 | EXPECT_EQ(ExpectedBlock1Str, Block1Dump); |
755 | |
756 | // Ensure that numbering is good when dumping the second block in isolation. |
757 | const char *ExpectedBlock2Str = R"(bb2: |
758 | EMIT vp<%5> = mul vp<%3>, vp<%2> |
759 | EMIT ret vp<%5> |
760 | No successors |
761 | )" ; |
762 | std::string Block2Dump; |
763 | raw_string_ostream OS2(Block2Dump); |
764 | VPBB2->print(O&: OS2); |
765 | EXPECT_EQ(ExpectedBlock2Str, Block2Dump); |
766 | |
767 | { |
768 | std::string I3Dump; |
769 | raw_string_ostream OS(I3Dump); |
770 | VPSlotTracker SlotTracker(&Plan); |
771 | I3->print(O&: OS, Indent: "" , SlotTracker); |
772 | OS.flush(); |
773 | EXPECT_EQ("EMIT br vp<%2>, vp<%3>" , I3Dump); |
774 | } |
775 | |
776 | { |
777 | std::string I4Dump; |
778 | raw_string_ostream OS(I4Dump); |
779 | OS << *I4; |
780 | OS.flush(); |
781 | EXPECT_EQ("EMIT vp<%5> = mul vp<%3>, vp<%2>" , I4Dump); |
782 | } |
783 | } |
784 | |
785 | TEST(VPBasicBlockTest, printPlanWithVFsAndUFs) { |
786 | |
787 | VPInstruction *TC = new VPInstruction(Instruction::Sub, {}); |
788 | VPBasicBlock *VPBB0 = new VPBasicBlock("preheader" ); |
789 | VPBB0->appendRecipe(Recipe: TC); |
790 | |
791 | VPInstruction *I1 = new VPInstruction(Instruction::Add, {}); |
792 | VPBasicBlock *VPBB1 = new VPBasicBlock(); |
793 | VPBB1->appendRecipe(Recipe: I1); |
794 | VPBB1->setName("bb1" ); |
795 | |
796 | VPlan Plan(VPBB0, TC, VPBB1); |
797 | Plan.setName("TestPlan" ); |
798 | Plan.addVF(VF: ElementCount::getFixed(MinVal: 4)); |
799 | |
800 | { |
801 | std::string FullDump; |
802 | raw_string_ostream OS(FullDump); |
803 | Plan.print(O&: OS); |
804 | |
805 | const char *ExpectedStr = R"(VPlan 'TestPlan for VF={4},UF>=1' { |
806 | vp<%1> = original trip-count |
807 | |
808 | preheader: |
809 | EMIT vp<%1> = sub |
810 | No successors |
811 | |
812 | bb1: |
813 | EMIT vp<%2> = add |
814 | No successors |
815 | } |
816 | )" ; |
817 | EXPECT_EQ(ExpectedStr, FullDump); |
818 | } |
819 | |
820 | { |
821 | Plan.addVF(VF: ElementCount::getScalable(MinVal: 8)); |
822 | std::string FullDump; |
823 | raw_string_ostream OS(FullDump); |
824 | Plan.print(O&: OS); |
825 | |
826 | const char *ExpectedStr = R"(VPlan 'TestPlan for VF={4,vscale x 8},UF>=1' { |
827 | vp<%1> = original trip-count |
828 | |
829 | preheader: |
830 | EMIT vp<%1> = sub |
831 | No successors |
832 | |
833 | bb1: |
834 | EMIT vp<%2> = add |
835 | No successors |
836 | } |
837 | )" ; |
838 | EXPECT_EQ(ExpectedStr, FullDump); |
839 | } |
840 | |
841 | { |
842 | Plan.setUF(4); |
843 | std::string FullDump; |
844 | raw_string_ostream OS(FullDump); |
845 | Plan.print(O&: OS); |
846 | |
847 | const char *ExpectedStr = R"(VPlan 'TestPlan for VF={4,vscale x 8},UF={4}' { |
848 | vp<%1> = original trip-count |
849 | |
850 | preheader: |
851 | EMIT vp<%1> = sub |
852 | No successors |
853 | |
854 | bb1: |
855 | EMIT vp<%2> = add |
856 | No successors |
857 | } |
858 | )" ; |
859 | EXPECT_EQ(ExpectedStr, FullDump); |
860 | } |
861 | } |
862 | #endif |
863 | |
864 | TEST(VPRecipeTest, CastVPInstructionToVPUser) { |
865 | VPValue Op1; |
866 | VPValue Op2; |
867 | VPInstruction Recipe(Instruction::Add, {&Op1, &Op2}); |
868 | EXPECT_TRUE(isa<VPUser>(&Recipe)); |
869 | VPRecipeBase *BaseR = &Recipe; |
870 | EXPECT_TRUE(isa<VPUser>(BaseR)); |
871 | EXPECT_EQ(&Recipe, BaseR); |
872 | } |
873 | |
874 | TEST(VPRecipeTest, CastVPWidenRecipeToVPUser) { |
875 | LLVMContext C; |
876 | |
877 | IntegerType *Int32 = IntegerType::get(C, NumBits: 32); |
878 | auto *AI = |
879 | BinaryOperator::CreateAdd(V1: UndefValue::get(T: Int32), V2: UndefValue::get(T: Int32)); |
880 | VPValue Op1; |
881 | VPValue Op2; |
882 | SmallVector<VPValue *, 2> Args; |
883 | Args.push_back(Elt: &Op1); |
884 | Args.push_back(Elt: &Op1); |
885 | VPWidenRecipe WidenR(*AI, make_range(x: Args.begin(), y: Args.end())); |
886 | EXPECT_TRUE(isa<VPUser>(&WidenR)); |
887 | VPRecipeBase *WidenRBase = &WidenR; |
888 | EXPECT_TRUE(isa<VPUser>(WidenRBase)); |
889 | EXPECT_EQ(&WidenR, WidenRBase); |
890 | delete AI; |
891 | } |
892 | |
893 | TEST(VPRecipeTest, CastVPWidenCallRecipeToVPUserAndVPDef) { |
894 | LLVMContext C; |
895 | |
896 | IntegerType *Int32 = IntegerType::get(C, NumBits: 32); |
897 | FunctionType *FTy = FunctionType::get(Result: Int32, isVarArg: false); |
898 | auto *Call = CallInst::Create(Ty: FTy, F: UndefValue::get(T: FTy)); |
899 | VPValue Op1; |
900 | VPValue Op2; |
901 | SmallVector<VPValue *, 2> Args; |
902 | Args.push_back(Elt: &Op1); |
903 | Args.push_back(Elt: &Op2); |
904 | VPWidenCallRecipe Recipe(*Call, make_range(x: Args.begin(), y: Args.end()), false); |
905 | EXPECT_TRUE(isa<VPUser>(&Recipe)); |
906 | VPRecipeBase *BaseR = &Recipe; |
907 | EXPECT_TRUE(isa<VPUser>(BaseR)); |
908 | EXPECT_EQ(&Recipe, BaseR); |
909 | |
910 | VPValue *VPV = &Recipe; |
911 | EXPECT_TRUE(VPV->getDefiningRecipe()); |
912 | EXPECT_EQ(&Recipe, VPV->getDefiningRecipe()); |
913 | |
914 | delete Call; |
915 | } |
916 | |
917 | TEST(VPRecipeTest, CastVPWidenSelectRecipeToVPUserAndVPDef) { |
918 | LLVMContext C; |
919 | |
920 | IntegerType *Int1 = IntegerType::get(C, NumBits: 1); |
921 | IntegerType *Int32 = IntegerType::get(C, NumBits: 32); |
922 | auto *SelectI = SelectInst::Create( |
923 | C: UndefValue::get(T: Int1), S1: UndefValue::get(T: Int32), S2: UndefValue::get(T: Int32)); |
924 | VPValue Op1; |
925 | VPValue Op2; |
926 | VPValue Op3; |
927 | SmallVector<VPValue *, 4> Args; |
928 | Args.push_back(Elt: &Op1); |
929 | Args.push_back(Elt: &Op2); |
930 | Args.push_back(Elt: &Op3); |
931 | VPWidenSelectRecipe WidenSelectR(*SelectI, |
932 | make_range(x: Args.begin(), y: Args.end())); |
933 | EXPECT_TRUE(isa<VPUser>(&WidenSelectR)); |
934 | VPRecipeBase *BaseR = &WidenSelectR; |
935 | EXPECT_TRUE(isa<VPUser>(BaseR)); |
936 | EXPECT_EQ(&WidenSelectR, BaseR); |
937 | |
938 | VPValue *VPV = &WidenSelectR; |
939 | EXPECT_TRUE(isa<VPRecipeBase>(VPV->getDefiningRecipe())); |
940 | EXPECT_EQ(&WidenSelectR, VPV->getDefiningRecipe()); |
941 | |
942 | delete SelectI; |
943 | } |
944 | |
945 | TEST(VPRecipeTest, CastVPWidenGEPRecipeToVPUserAndVPDef) { |
946 | LLVMContext C; |
947 | |
948 | IntegerType *Int32 = IntegerType::get(C, NumBits: 32); |
949 | PointerType *Int32Ptr = PointerType::get(ElementType: Int32, AddressSpace: 0); |
950 | auto *GEP = GetElementPtrInst::Create(PointeeType: Int32, Ptr: UndefValue::get(T: Int32Ptr), |
951 | IdxList: UndefValue::get(T: Int32)); |
952 | VPValue Op1; |
953 | VPValue Op2; |
954 | SmallVector<VPValue *, 4> Args; |
955 | Args.push_back(Elt: &Op1); |
956 | Args.push_back(Elt: &Op2); |
957 | VPWidenGEPRecipe Recipe(GEP, make_range(x: Args.begin(), y: Args.end())); |
958 | EXPECT_TRUE(isa<VPUser>(&Recipe)); |
959 | VPRecipeBase *BaseR = &Recipe; |
960 | EXPECT_TRUE(isa<VPUser>(BaseR)); |
961 | EXPECT_EQ(&Recipe, BaseR); |
962 | |
963 | VPValue *VPV = &Recipe; |
964 | EXPECT_TRUE(isa<VPRecipeBase>(VPV->getDefiningRecipe())); |
965 | EXPECT_EQ(&Recipe, VPV->getDefiningRecipe()); |
966 | |
967 | delete GEP; |
968 | } |
969 | |
970 | TEST(VPRecipeTest, CastVPBlendRecipeToVPUser) { |
971 | LLVMContext C; |
972 | |
973 | IntegerType *Int32 = IntegerType::get(C, NumBits: 32); |
974 | auto *Phi = PHINode::Create(Ty: Int32, NumReservedValues: 1); |
975 | VPValue I1; |
976 | VPValue I2; |
977 | VPValue M2; |
978 | SmallVector<VPValue *, 4> Args; |
979 | Args.push_back(Elt: &I1); |
980 | Args.push_back(Elt: &I2); |
981 | Args.push_back(Elt: &M2); |
982 | VPBlendRecipe Recipe(Phi, Args); |
983 | EXPECT_TRUE(isa<VPUser>(&Recipe)); |
984 | VPRecipeBase *BaseR = &Recipe; |
985 | EXPECT_TRUE(isa<VPUser>(BaseR)); |
986 | delete Phi; |
987 | } |
988 | |
989 | TEST(VPRecipeTest, CastVPInterleaveRecipeToVPUser) { |
990 | LLVMContext C; |
991 | |
992 | VPValue Addr; |
993 | VPValue Mask; |
994 | InterleaveGroup<Instruction> IG(4, false, Align(4)); |
995 | VPInterleaveRecipe Recipe(&IG, &Addr, {}, &Mask, false); |
996 | EXPECT_TRUE(isa<VPUser>(&Recipe)); |
997 | VPRecipeBase *BaseR = &Recipe; |
998 | EXPECT_TRUE(isa<VPUser>(BaseR)); |
999 | EXPECT_EQ(&Recipe, BaseR); |
1000 | } |
1001 | |
1002 | TEST(VPRecipeTest, CastVPReplicateRecipeToVPUser) { |
1003 | LLVMContext C; |
1004 | |
1005 | VPValue Op1; |
1006 | VPValue Op2; |
1007 | SmallVector<VPValue *, 4> Args; |
1008 | Args.push_back(Elt: &Op1); |
1009 | Args.push_back(Elt: &Op2); |
1010 | |
1011 | IntegerType *Int32 = IntegerType::get(C, NumBits: 32); |
1012 | FunctionType *FTy = FunctionType::get(Result: Int32, isVarArg: false); |
1013 | auto *Call = CallInst::Create(Ty: FTy, F: UndefValue::get(T: FTy)); |
1014 | VPReplicateRecipe Recipe(Call, make_range(x: Args.begin(), y: Args.end()), true); |
1015 | EXPECT_TRUE(isa<VPUser>(&Recipe)); |
1016 | VPRecipeBase *BaseR = &Recipe; |
1017 | EXPECT_TRUE(isa<VPUser>(BaseR)); |
1018 | delete Call; |
1019 | } |
1020 | |
1021 | TEST(VPRecipeTest, CastVPBranchOnMaskRecipeToVPUser) { |
1022 | LLVMContext C; |
1023 | |
1024 | VPValue Mask; |
1025 | VPBranchOnMaskRecipe Recipe(&Mask); |
1026 | EXPECT_TRUE(isa<VPUser>(&Recipe)); |
1027 | VPRecipeBase *BaseR = &Recipe; |
1028 | EXPECT_TRUE(isa<VPUser>(BaseR)); |
1029 | EXPECT_EQ(&Recipe, BaseR); |
1030 | } |
1031 | |
1032 | TEST(VPRecipeTest, CastVPWidenMemoryRecipeToVPUserAndVPDef) { |
1033 | LLVMContext C; |
1034 | |
1035 | IntegerType *Int32 = IntegerType::get(C, NumBits: 32); |
1036 | PointerType *Int32Ptr = PointerType::get(ElementType: Int32, AddressSpace: 0); |
1037 | auto *Load = |
1038 | new LoadInst(Int32, UndefValue::get(T: Int32Ptr), "" , false, Align(1)); |
1039 | VPValue Addr; |
1040 | VPValue Mask; |
1041 | VPWidenLoadRecipe Recipe(*Load, &Addr, &Mask, true, false, {}); |
1042 | EXPECT_TRUE(isa<VPUser>(&Recipe)); |
1043 | VPRecipeBase *BaseR = &Recipe; |
1044 | EXPECT_TRUE(isa<VPUser>(BaseR)); |
1045 | EXPECT_EQ(&Recipe, BaseR); |
1046 | |
1047 | VPValue *VPV = Recipe.getVPSingleValue(); |
1048 | EXPECT_TRUE(isa<VPRecipeBase>(VPV->getDefiningRecipe())); |
1049 | EXPECT_EQ(&Recipe, VPV->getDefiningRecipe()); |
1050 | |
1051 | delete Load; |
1052 | } |
1053 | |
1054 | TEST(VPRecipeTest, MayHaveSideEffectsAndMayReadWriteMemory) { |
1055 | LLVMContext C; |
1056 | IntegerType *Int1 = IntegerType::get(C, NumBits: 1); |
1057 | IntegerType *Int32 = IntegerType::get(C, NumBits: 32); |
1058 | PointerType *Int32Ptr = PointerType::get(ElementType: Int32, AddressSpace: 0); |
1059 | |
1060 | { |
1061 | auto *AI = BinaryOperator::CreateAdd(V1: UndefValue::get(T: Int32), |
1062 | V2: UndefValue::get(T: Int32)); |
1063 | VPValue Op1; |
1064 | VPValue Op2; |
1065 | SmallVector<VPValue *, 2> Args; |
1066 | Args.push_back(Elt: &Op1); |
1067 | Args.push_back(Elt: &Op1); |
1068 | VPWidenRecipe Recipe(*AI, make_range(x: Args.begin(), y: Args.end())); |
1069 | EXPECT_FALSE(Recipe.mayHaveSideEffects()); |
1070 | EXPECT_FALSE(Recipe.mayReadFromMemory()); |
1071 | EXPECT_FALSE(Recipe.mayWriteToMemory()); |
1072 | EXPECT_FALSE(Recipe.mayReadOrWriteMemory()); |
1073 | delete AI; |
1074 | } |
1075 | |
1076 | { |
1077 | auto *SelectI = SelectInst::Create( |
1078 | C: UndefValue::get(T: Int1), S1: UndefValue::get(T: Int32), S2: UndefValue::get(T: Int32)); |
1079 | VPValue Op1; |
1080 | VPValue Op2; |
1081 | VPValue Op3; |
1082 | SmallVector<VPValue *, 4> Args; |
1083 | Args.push_back(Elt: &Op1); |
1084 | Args.push_back(Elt: &Op2); |
1085 | Args.push_back(Elt: &Op3); |
1086 | VPWidenSelectRecipe Recipe(*SelectI, make_range(x: Args.begin(), y: Args.end())); |
1087 | EXPECT_FALSE(Recipe.mayHaveSideEffects()); |
1088 | EXPECT_FALSE(Recipe.mayReadFromMemory()); |
1089 | EXPECT_FALSE(Recipe.mayWriteToMemory()); |
1090 | EXPECT_FALSE(Recipe.mayReadOrWriteMemory()); |
1091 | delete SelectI; |
1092 | } |
1093 | |
1094 | { |
1095 | auto *GEP = GetElementPtrInst::Create(PointeeType: Int32, Ptr: UndefValue::get(T: Int32Ptr), |
1096 | IdxList: UndefValue::get(T: Int32)); |
1097 | VPValue Op1; |
1098 | VPValue Op2; |
1099 | SmallVector<VPValue *, 4> Args; |
1100 | Args.push_back(Elt: &Op1); |
1101 | Args.push_back(Elt: &Op2); |
1102 | VPWidenGEPRecipe Recipe(GEP, make_range(x: Args.begin(), y: Args.end())); |
1103 | EXPECT_FALSE(Recipe.mayHaveSideEffects()); |
1104 | EXPECT_FALSE(Recipe.mayReadFromMemory()); |
1105 | EXPECT_FALSE(Recipe.mayWriteToMemory()); |
1106 | EXPECT_FALSE(Recipe.mayReadOrWriteMemory()); |
1107 | delete GEP; |
1108 | } |
1109 | |
1110 | { |
1111 | VPValue Mask; |
1112 | VPBranchOnMaskRecipe Recipe(&Mask); |
1113 | EXPECT_TRUE(Recipe.mayHaveSideEffects()); |
1114 | EXPECT_FALSE(Recipe.mayReadFromMemory()); |
1115 | EXPECT_FALSE(Recipe.mayWriteToMemory()); |
1116 | EXPECT_FALSE(Recipe.mayReadOrWriteMemory()); |
1117 | } |
1118 | |
1119 | { |
1120 | VPValue ChainOp; |
1121 | VPValue VecOp; |
1122 | VPValue CondOp; |
1123 | VPReductionRecipe Recipe(RecurrenceDescriptor(), nullptr, &ChainOp, &CondOp, |
1124 | &VecOp, false); |
1125 | EXPECT_FALSE(Recipe.mayHaveSideEffects()); |
1126 | EXPECT_FALSE(Recipe.mayReadFromMemory()); |
1127 | EXPECT_FALSE(Recipe.mayWriteToMemory()); |
1128 | EXPECT_FALSE(Recipe.mayReadOrWriteMemory()); |
1129 | } |
1130 | |
1131 | { |
1132 | auto *Load = |
1133 | new LoadInst(Int32, UndefValue::get(T: Int32Ptr), "" , false, Align(1)); |
1134 | VPValue Addr; |
1135 | VPValue Mask; |
1136 | VPWidenLoadRecipe Recipe(*Load, &Addr, &Mask, true, false, {}); |
1137 | EXPECT_FALSE(Recipe.mayHaveSideEffects()); |
1138 | EXPECT_TRUE(Recipe.mayReadFromMemory()); |
1139 | EXPECT_FALSE(Recipe.mayWriteToMemory()); |
1140 | EXPECT_TRUE(Recipe.mayReadOrWriteMemory()); |
1141 | delete Load; |
1142 | } |
1143 | |
1144 | { |
1145 | auto *Store = new StoreInst(UndefValue::get(T: Int32), |
1146 | UndefValue::get(T: Int32Ptr), false, Align(1)); |
1147 | VPValue Addr; |
1148 | VPValue Mask; |
1149 | VPValue StoredV; |
1150 | VPWidenStoreRecipe Recipe(*Store, &Addr, &StoredV, &Mask, false, false, {}); |
1151 | EXPECT_TRUE(Recipe.mayHaveSideEffects()); |
1152 | EXPECT_FALSE(Recipe.mayReadFromMemory()); |
1153 | EXPECT_TRUE(Recipe.mayWriteToMemory()); |
1154 | EXPECT_TRUE(Recipe.mayReadOrWriteMemory()); |
1155 | delete Store; |
1156 | } |
1157 | |
1158 | { |
1159 | FunctionType *FTy = FunctionType::get(Result: Int32, isVarArg: false); |
1160 | auto *Call = CallInst::Create(Ty: FTy, F: UndefValue::get(T: FTy)); |
1161 | VPValue Op1; |
1162 | VPValue Op2; |
1163 | SmallVector<VPValue *, 2> Args; |
1164 | Args.push_back(Elt: &Op1); |
1165 | Args.push_back(Elt: &Op2); |
1166 | VPWidenCallRecipe Recipe(*Call, make_range(x: Args.begin(), y: Args.end()), |
1167 | false); |
1168 | EXPECT_TRUE(Recipe.mayHaveSideEffects()); |
1169 | EXPECT_TRUE(Recipe.mayReadFromMemory()); |
1170 | EXPECT_TRUE(Recipe.mayWriteToMemory()); |
1171 | EXPECT_TRUE(Recipe.mayReadOrWriteMemory()); |
1172 | delete Call; |
1173 | } |
1174 | |
1175 | { |
1176 | // Test for a call to a function without side-effects. |
1177 | LLVMContext C; |
1178 | Module M("" , C); |
1179 | Function *TheFn = Intrinsic::getDeclaration(M: &M, Intrinsic::id: thread_pointer); |
1180 | |
1181 | auto *Call = CallInst::Create(Ty: TheFn->getFunctionType(), F: TheFn); |
1182 | VPValue Op1; |
1183 | VPValue Op2; |
1184 | SmallVector<VPValue *, 2> Args; |
1185 | Args.push_back(Elt: &Op1); |
1186 | Args.push_back(Elt: &Op2); |
1187 | VPWidenCallRecipe Recipe(*Call, make_range(x: Args.begin(), y: Args.end()), |
1188 | false); |
1189 | EXPECT_FALSE(Recipe.mayHaveSideEffects()); |
1190 | EXPECT_FALSE(Recipe.mayReadFromMemory()); |
1191 | EXPECT_FALSE(Recipe.mayWriteToMemory()); |
1192 | EXPECT_FALSE(Recipe.mayReadOrWriteMemory()); |
1193 | delete Call; |
1194 | } |
1195 | |
1196 | { |
1197 | VPValue Op1; |
1198 | VPValue Op2; |
1199 | InductionDescriptor IndDesc; |
1200 | VPScalarIVStepsRecipe Recipe(IndDesc, &Op1, &Op2); |
1201 | EXPECT_FALSE(Recipe.mayHaveSideEffects()); |
1202 | EXPECT_FALSE(Recipe.mayReadFromMemory()); |
1203 | EXPECT_FALSE(Recipe.mayWriteToMemory()); |
1204 | EXPECT_FALSE(Recipe.mayReadOrWriteMemory()); |
1205 | } |
1206 | |
1207 | // The initial implementation is conservative with respect to VPInstructions. |
1208 | { |
1209 | VPValue Op1; |
1210 | VPValue Op2; |
1211 | VPInstruction VPInst(Instruction::Add, {&Op1, &Op2}); |
1212 | VPRecipeBase &Recipe = VPInst; |
1213 | EXPECT_TRUE(Recipe.mayHaveSideEffects()); |
1214 | EXPECT_TRUE(Recipe.mayReadFromMemory()); |
1215 | EXPECT_TRUE(Recipe.mayWriteToMemory()); |
1216 | EXPECT_TRUE(Recipe.mayReadOrWriteMemory()); |
1217 | } |
1218 | { |
1219 | VPValue Op1; |
1220 | VPPredInstPHIRecipe Recipe(&Op1); |
1221 | EXPECT_FALSE(Recipe.mayHaveSideEffects()); |
1222 | EXPECT_FALSE(Recipe.mayReadFromMemory()); |
1223 | EXPECT_FALSE(Recipe.mayWriteToMemory()); |
1224 | EXPECT_FALSE(Recipe.mayReadOrWriteMemory()); |
1225 | } |
1226 | } |
1227 | |
1228 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
1229 | TEST(VPRecipeTest, dumpRecipeInPlan) { |
1230 | VPBasicBlock *VPBB0 = new VPBasicBlock("preheader" ); |
1231 | VPBasicBlock *VPBB1 = new VPBasicBlock(); |
1232 | VPlan Plan(VPBB0, VPBB1); |
1233 | |
1234 | LLVMContext C; |
1235 | |
1236 | IntegerType *Int32 = IntegerType::get(C, NumBits: 32); |
1237 | auto *AI = |
1238 | BinaryOperator::CreateAdd(V1: UndefValue::get(T: Int32), V2: UndefValue::get(T: Int32)); |
1239 | AI->setName("a" ); |
1240 | SmallVector<VPValue *, 2> Args; |
1241 | VPValue *ExtVPV1 = Plan.getOrAddLiveIn(V: ConstantInt::get(Ty: Int32, V: 1)); |
1242 | VPValue *ExtVPV2 = Plan.getOrAddLiveIn(V: ConstantInt::get(Ty: Int32, V: 2)); |
1243 | Args.push_back(Elt: ExtVPV1); |
1244 | Args.push_back(Elt: ExtVPV2); |
1245 | VPWidenRecipe *WidenR = |
1246 | new VPWidenRecipe(*AI, make_range(x: Args.begin(), y: Args.end())); |
1247 | VPBB1->appendRecipe(Recipe: WidenR); |
1248 | |
1249 | { |
1250 | // Use EXPECT_EXIT to capture stderr and compare against expected output. |
1251 | // |
1252 | // Test VPValue::dump(). |
1253 | VPValue *VPV = WidenR; |
1254 | EXPECT_EXIT( |
1255 | { |
1256 | VPV->dump(); |
1257 | exit(0); |
1258 | }, |
1259 | testing::ExitedWithCode(0), "WIDEN ir<%a> = add ir<1>, ir<2>" ); |
1260 | |
1261 | // Test VPRecipeBase::dump(). |
1262 | VPRecipeBase *R = WidenR; |
1263 | EXPECT_EXIT( |
1264 | { |
1265 | R->dump(); |
1266 | exit(0); |
1267 | }, |
1268 | testing::ExitedWithCode(0), "WIDEN ir<%a> = add ir<1>, ir<2>" ); |
1269 | |
1270 | // Test VPDef::dump(). |
1271 | VPDef *D = WidenR; |
1272 | EXPECT_EXIT( |
1273 | { |
1274 | D->dump(); |
1275 | exit(0); |
1276 | }, |
1277 | testing::ExitedWithCode(0), "WIDEN ir<%a> = add ir<1>, ir<2>" ); |
1278 | } |
1279 | |
1280 | delete AI; |
1281 | } |
1282 | |
1283 | TEST(VPRecipeTest, dumpRecipeUnnamedVPValuesInPlan) { |
1284 | VPBasicBlock *VPBB0 = new VPBasicBlock("preheader" ); |
1285 | VPBasicBlock *VPBB1 = new VPBasicBlock(); |
1286 | VPlan Plan(VPBB0, VPBB1); |
1287 | |
1288 | LLVMContext C; |
1289 | |
1290 | IntegerType *Int32 = IntegerType::get(C, NumBits: 32); |
1291 | auto *AI = |
1292 | BinaryOperator::CreateAdd(V1: UndefValue::get(T: Int32), V2: UndefValue::get(T: Int32)); |
1293 | AI->setName("a" ); |
1294 | SmallVector<VPValue *, 2> Args; |
1295 | VPValue *ExtVPV1 = Plan.getOrAddLiveIn(V: ConstantInt::get(Ty: Int32, V: 1)); |
1296 | VPValue *ExtVPV2 = Plan.getOrAddLiveIn(V: AI); |
1297 | Args.push_back(Elt: ExtVPV1); |
1298 | Args.push_back(Elt: ExtVPV2); |
1299 | VPInstruction *I1 = new VPInstruction(Instruction::Add, {ExtVPV1, ExtVPV2}); |
1300 | VPInstruction *I2 = new VPInstruction(Instruction::Mul, {I1, I1}); |
1301 | VPBB1->appendRecipe(Recipe: I1); |
1302 | VPBB1->appendRecipe(Recipe: I2); |
1303 | |
1304 | // Check printing I1. |
1305 | { |
1306 | // Use EXPECT_EXIT to capture stderr and compare against expected output. |
1307 | // |
1308 | // Test VPValue::dump(). |
1309 | VPValue *VPV = I1; |
1310 | EXPECT_EXIT( |
1311 | { |
1312 | VPV->dump(); |
1313 | exit(0); |
1314 | }, |
1315 | testing::ExitedWithCode(0), "EMIT vp<%1> = add ir<1>, ir<%a>" ); |
1316 | |
1317 | // Test VPRecipeBase::dump(). |
1318 | VPRecipeBase *R = I1; |
1319 | EXPECT_EXIT( |
1320 | { |
1321 | R->dump(); |
1322 | exit(0); |
1323 | }, |
1324 | testing::ExitedWithCode(0), "EMIT vp<%1> = add ir<1>, ir<%a>" ); |
1325 | |
1326 | // Test VPDef::dump(). |
1327 | VPDef *D = I1; |
1328 | EXPECT_EXIT( |
1329 | { |
1330 | D->dump(); |
1331 | exit(0); |
1332 | }, |
1333 | testing::ExitedWithCode(0), "EMIT vp<%1> = add ir<1>, ir<%a>" ); |
1334 | } |
1335 | // Check printing I2. |
1336 | { |
1337 | // Use EXPECT_EXIT to capture stderr and compare against expected output. |
1338 | // |
1339 | // Test VPValue::dump(). |
1340 | VPValue *VPV = I2; |
1341 | EXPECT_EXIT( |
1342 | { |
1343 | VPV->dump(); |
1344 | exit(0); |
1345 | }, |
1346 | testing::ExitedWithCode(0), "EMIT vp<%2> = mul vp<%1>, vp<%1>" ); |
1347 | |
1348 | // Test VPRecipeBase::dump(). |
1349 | VPRecipeBase *R = I2; |
1350 | EXPECT_EXIT( |
1351 | { |
1352 | R->dump(); |
1353 | exit(0); |
1354 | }, |
1355 | testing::ExitedWithCode(0), "EMIT vp<%2> = mul vp<%1>, vp<%1>" ); |
1356 | |
1357 | // Test VPDef::dump(). |
1358 | VPDef *D = I2; |
1359 | EXPECT_EXIT( |
1360 | { |
1361 | D->dump(); |
1362 | exit(0); |
1363 | }, |
1364 | testing::ExitedWithCode(0), "EMIT vp<%2> = mul vp<%1>, vp<%1>" ); |
1365 | } |
1366 | delete AI; |
1367 | } |
1368 | |
1369 | TEST(VPRecipeTest, dumpRecipeUnnamedVPValuesNotInPlanOrBlock) { |
1370 | LLVMContext C; |
1371 | IntegerType *Int32 = IntegerType::get(C, NumBits: 32); |
1372 | auto *AI = |
1373 | BinaryOperator::CreateAdd(V1: UndefValue::get(T: Int32), V2: UndefValue::get(T: Int32)); |
1374 | AI->setName("a" ); |
1375 | VPValue *ExtVPV1 = new VPValue(ConstantInt::get(Ty: Int32, V: 1)); |
1376 | VPValue *ExtVPV2 = new VPValue(AI); |
1377 | |
1378 | VPInstruction *I1 = new VPInstruction(Instruction::Add, {ExtVPV1, ExtVPV2}); |
1379 | VPInstruction *I2 = new VPInstruction(Instruction::Mul, {I1, I1}); |
1380 | |
1381 | // Check printing I1. |
1382 | { |
1383 | // Use EXPECT_EXIT to capture stderr and compare against expected output. |
1384 | // |
1385 | // Test VPValue::dump(). |
1386 | VPValue *VPV = I1; |
1387 | EXPECT_EXIT( |
1388 | { |
1389 | VPV->dump(); |
1390 | exit(0); |
1391 | }, |
1392 | testing::ExitedWithCode(0), "EMIT <badref> = add ir<1>, ir<%a>" ); |
1393 | |
1394 | // Test VPRecipeBase::dump(). |
1395 | VPRecipeBase *R = I1; |
1396 | EXPECT_EXIT( |
1397 | { |
1398 | R->dump(); |
1399 | exit(0); |
1400 | }, |
1401 | testing::ExitedWithCode(0), "EMIT <badref> = add ir<1>, ir<%a>" ); |
1402 | |
1403 | // Test VPDef::dump(). |
1404 | VPDef *D = I1; |
1405 | EXPECT_EXIT( |
1406 | { |
1407 | D->dump(); |
1408 | exit(0); |
1409 | }, |
1410 | testing::ExitedWithCode(0), "EMIT <badref> = add ir<1>, ir<%a>" ); |
1411 | } |
1412 | // Check printing I2. |
1413 | { |
1414 | // Use EXPECT_EXIT to capture stderr and compare against expected output. |
1415 | // |
1416 | // Test VPValue::dump(). |
1417 | VPValue *VPV = I2; |
1418 | EXPECT_EXIT( |
1419 | { |
1420 | VPV->dump(); |
1421 | exit(0); |
1422 | }, |
1423 | testing::ExitedWithCode(0), "EMIT <badref> = mul <badref>, <badref>" ); |
1424 | |
1425 | // Test VPRecipeBase::dump(). |
1426 | VPRecipeBase *R = I2; |
1427 | EXPECT_EXIT( |
1428 | { |
1429 | R->dump(); |
1430 | exit(0); |
1431 | }, |
1432 | testing::ExitedWithCode(0), "EMIT <badref> = mul <badref>, <badref>" ); |
1433 | |
1434 | // Test VPDef::dump(). |
1435 | VPDef *D = I2; |
1436 | EXPECT_EXIT( |
1437 | { |
1438 | D->dump(); |
1439 | exit(0); |
1440 | }, |
1441 | testing::ExitedWithCode(0), "EMIT <badref> = mul <badref>, <badref>" ); |
1442 | } |
1443 | |
1444 | delete I2; |
1445 | delete I1; |
1446 | delete ExtVPV2; |
1447 | delete ExtVPV1; |
1448 | delete AI; |
1449 | } |
1450 | |
1451 | #endif |
1452 | |
1453 | TEST(VPRecipeTest, CastVPReductionRecipeToVPUser) { |
1454 | LLVMContext C; |
1455 | |
1456 | VPValue ChainOp; |
1457 | VPValue VecOp; |
1458 | VPValue CondOp; |
1459 | VPReductionRecipe Recipe(RecurrenceDescriptor(), nullptr, &ChainOp, &CondOp, |
1460 | &VecOp, false); |
1461 | EXPECT_TRUE(isa<VPUser>(&Recipe)); |
1462 | VPRecipeBase *BaseR = &Recipe; |
1463 | EXPECT_TRUE(isa<VPUser>(BaseR)); |
1464 | } |
1465 | |
1466 | struct VPDoubleValueDef : public VPRecipeBase { |
1467 | VPDoubleValueDef(ArrayRef<VPValue *> Operands) : VPRecipeBase(99, Operands) { |
1468 | new VPValue(nullptr, this); |
1469 | new VPValue(nullptr, this); |
1470 | } |
1471 | |
1472 | VPRecipeBase *clone() override { return nullptr; } |
1473 | |
1474 | void execute(struct VPTransformState &State) override {} |
1475 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
1476 | void print(raw_ostream &O, const Twine &Indent, |
1477 | VPSlotTracker &SlotTracker) const override {} |
1478 | #endif |
1479 | }; |
1480 | |
1481 | TEST(VPDoubleValueDefTest, traverseUseLists) { |
1482 | // Check that the def-use chains of a multi-def can be traversed in both |
1483 | // directions. |
1484 | |
1485 | // Create a new VPDef which defines 2 values and has 2 operands. |
1486 | VPInstruction Op0(20, {}); |
1487 | VPInstruction Op1(30, {}); |
1488 | VPDoubleValueDef DoubleValueDef({&Op0, &Op1}); |
1489 | |
1490 | // Create a new users of the defined values. |
1491 | VPInstruction I1( |
1492 | 1, {DoubleValueDef.getVPValue(I: 0), DoubleValueDef.getVPValue(I: 1)}); |
1493 | VPInstruction I2(2, {DoubleValueDef.getVPValue(I: 0)}); |
1494 | VPInstruction I3(3, {DoubleValueDef.getVPValue(I: 1)}); |
1495 | |
1496 | // Check operands of the VPDef (traversing upwards). |
1497 | SmallVector<VPValue *, 4> DoubleOperands(DoubleValueDef.op_begin(), |
1498 | DoubleValueDef.op_end()); |
1499 | EXPECT_EQ(2u, DoubleOperands.size()); |
1500 | EXPECT_EQ(&Op0, DoubleOperands[0]); |
1501 | EXPECT_EQ(&Op1, DoubleOperands[1]); |
1502 | |
1503 | // Check users of the defined values (traversing downwards). |
1504 | SmallVector<VPUser *, 4> DoubleValueDefV0Users( |
1505 | DoubleValueDef.getVPValue(I: 0)->user_begin(), |
1506 | DoubleValueDef.getVPValue(I: 0)->user_end()); |
1507 | EXPECT_EQ(2u, DoubleValueDefV0Users.size()); |
1508 | EXPECT_EQ(&I1, DoubleValueDefV0Users[0]); |
1509 | EXPECT_EQ(&I2, DoubleValueDefV0Users[1]); |
1510 | |
1511 | SmallVector<VPUser *, 4> DoubleValueDefV1Users( |
1512 | DoubleValueDef.getVPValue(I: 1)->user_begin(), |
1513 | DoubleValueDef.getVPValue(I: 1)->user_end()); |
1514 | EXPECT_EQ(2u, DoubleValueDefV1Users.size()); |
1515 | EXPECT_EQ(&I1, DoubleValueDefV1Users[0]); |
1516 | EXPECT_EQ(&I3, DoubleValueDefV1Users[1]); |
1517 | |
1518 | // Now check that we can get the right VPDef for each defined value. |
1519 | EXPECT_EQ(&DoubleValueDef, I1.getOperand(0)->getDefiningRecipe()); |
1520 | EXPECT_EQ(&DoubleValueDef, I1.getOperand(1)->getDefiningRecipe()); |
1521 | EXPECT_EQ(&DoubleValueDef, I2.getOperand(0)->getDefiningRecipe()); |
1522 | EXPECT_EQ(&DoubleValueDef, I3.getOperand(0)->getDefiningRecipe()); |
1523 | } |
1524 | |
1525 | } // namespace |
1526 | } // namespace llvm |
1527 | |