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

1606 lines
48 KiB
C++

//===- llvm/unittests/Transforms/Vectorize/VPlanTest.cpp - VPlan tests ----===//
//
//
// 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/VPlanCFG.h"
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/Analysis/VectorUtils.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "gtest/gtest.h"
#include <string>
namespace llvm {
namespace {
#define CHECK_ITERATOR(Range1, ...) \
do { \
std::vector<VPInstruction *> Tmp = {__VA_ARGS__}; \
EXPECT_EQ((size_t)std::distance(Range1.begin(), Range1.end()), \
Tmp.size()); \
for (auto Pair : zip(Range1, make_range(Tmp.begin(), Tmp.end()))) \
EXPECT_EQ(&std::get<0>(Pair), std::get<1>(Pair)); \
} while (0)
TEST(VPInstructionTest, insertBefore) {
VPInstruction *I1 = new VPInstruction(0, {});
VPInstruction *I2 = new VPInstruction(1, {});
VPInstruction *I3 = new VPInstruction(2, {});
VPBasicBlock VPBB1;
VPBB1.appendRecipe(I1);
I2->insertBefore(I1);
CHECK_ITERATOR(VPBB1, I2, I1);
I3->insertBefore(I2);
CHECK_ITERATOR(VPBB1, I3, I2, I1);
}
TEST(VPInstructionTest, eraseFromParent) {
VPInstruction *I1 = new VPInstruction(0, {});
VPInstruction *I2 = new VPInstruction(1, {});
VPInstruction *I3 = new VPInstruction(2, {});
VPBasicBlock VPBB1;
VPBB1.appendRecipe(I1);
VPBB1.appendRecipe(I2);
VPBB1.appendRecipe(I3);
I2->eraseFromParent();
CHECK_ITERATOR(VPBB1, I1, I3);
I1->eraseFromParent();
CHECK_ITERATOR(VPBB1, I3);
I3->eraseFromParent();
EXPECT_TRUE(VPBB1.empty());
}
TEST(VPInstructionTest, moveAfter) {
VPInstruction *I1 = new VPInstruction(0, {});
VPInstruction *I2 = new VPInstruction(1, {});
VPInstruction *I3 = new VPInstruction(2, {});
VPBasicBlock VPBB1;
VPBB1.appendRecipe(I1);
VPBB1.appendRecipe(I2);
VPBB1.appendRecipe(I3);
I1->moveAfter(I2);
CHECK_ITERATOR(VPBB1, I2, I1, I3);
VPInstruction *I4 = new VPInstruction(4, {});
VPInstruction *I5 = new VPInstruction(5, {});
VPBasicBlock VPBB2;
VPBB2.appendRecipe(I4);
VPBB2.appendRecipe(I5);
I3->moveAfter(I4);
CHECK_ITERATOR(VPBB1, I2, I1);
CHECK_ITERATOR(VPBB2, I4, I3, I5);
EXPECT_EQ(I3->getParent(), I4->getParent());
}
TEST(VPInstructionTest, moveBefore) {
VPInstruction *I1 = new VPInstruction(0, {});
VPInstruction *I2 = new VPInstruction(1, {});
VPInstruction *I3 = new VPInstruction(2, {});
VPBasicBlock VPBB1;
VPBB1.appendRecipe(I1);
VPBB1.appendRecipe(I2);
VPBB1.appendRecipe(I3);
I1->moveBefore(VPBB1, I3->getIterator());
CHECK_ITERATOR(VPBB1, I2, I1, I3);
VPInstruction *I4 = new VPInstruction(4, {});
VPInstruction *I5 = new VPInstruction(5, {});
VPBasicBlock VPBB2;
VPBB2.appendRecipe(I4);
VPBB2.appendRecipe(I5);
I3->moveBefore(VPBB2, I4->getIterator());
CHECK_ITERATOR(VPBB1, I2, I1);
CHECK_ITERATOR(VPBB2, I3, I4, I5);
EXPECT_EQ(I3->getParent(), I4->getParent());
VPBasicBlock VPBB3;
I4->moveBefore(VPBB3, VPBB3.end());
CHECK_ITERATOR(VPBB1, I2, I1);
CHECK_ITERATOR(VPBB2, I3, I5);
CHECK_ITERATOR(VPBB3, I4);
EXPECT_EQ(&VPBB3, I4->getParent());
}
TEST(VPInstructionTest, setOperand) {
VPValue *VPV1 = new VPValue();
VPValue *VPV2 = new VPValue();
VPInstruction *I1 = new VPInstruction(0, {VPV1, VPV2});
EXPECT_EQ(1u, VPV1->getNumUsers());
EXPECT_EQ(I1, *VPV1->user_begin());
EXPECT_EQ(1u, VPV2->getNumUsers());
EXPECT_EQ(I1, *VPV2->user_begin());
// Replace operand 0 (VPV1) with VPV3.
VPValue *VPV3 = new VPValue();
I1->setOperand(0, VPV3);
EXPECT_EQ(0u, VPV1->getNumUsers());
EXPECT_EQ(1u, VPV2->getNumUsers());
EXPECT_EQ(I1, *VPV2->user_begin());
EXPECT_EQ(1u, VPV3->getNumUsers());
EXPECT_EQ(I1, *VPV3->user_begin());
// Replace operand 1 (VPV2) with VPV3.
I1->setOperand(1, VPV3);
EXPECT_EQ(0u, VPV1->getNumUsers());
EXPECT_EQ(0u, VPV2->getNumUsers());
EXPECT_EQ(2u, VPV3->getNumUsers());
EXPECT_EQ(I1, *VPV3->user_begin());
EXPECT_EQ(I1, *std::next(VPV3->user_begin()));
// Replace operand 0 (VPV3) with VPV4.
VPValue *VPV4 = new VPValue();
I1->setOperand(0, VPV4);
EXPECT_EQ(1u, VPV3->getNumUsers());
EXPECT_EQ(I1, *VPV3->user_begin());
EXPECT_EQ(I1, *VPV4->user_begin());
// Replace operand 1 (VPV3) with VPV4.
I1->setOperand(1, VPV4);
EXPECT_EQ(0u, VPV3->getNumUsers());
EXPECT_EQ(I1, *VPV4->user_begin());
EXPECT_EQ(I1, *std::next(VPV4->user_begin()));
delete I1;
delete VPV1;
delete VPV2;
delete VPV3;
delete VPV4;
}
TEST(VPInstructionTest, replaceAllUsesWith) {
VPValue *VPV1 = new VPValue();
VPValue *VPV2 = new VPValue();
VPInstruction *I1 = new VPInstruction(0, {VPV1, VPV2});
// Replace all uses of VPV1 with VPV3.
VPValue *VPV3 = new VPValue();
VPV1->replaceAllUsesWith(VPV3);
EXPECT_EQ(VPV3, I1->getOperand(0));
EXPECT_EQ(VPV2, I1->getOperand(1));
EXPECT_EQ(0u, VPV1->getNumUsers());
EXPECT_EQ(1u, VPV2->getNumUsers());
EXPECT_EQ(I1, *VPV2->user_begin());
EXPECT_EQ(1u, VPV3->getNumUsers());
EXPECT_EQ(I1, *VPV3->user_begin());
// Replace all uses of VPV2 with VPV3.
VPV2->replaceAllUsesWith(VPV3);
EXPECT_EQ(VPV3, I1->getOperand(0));
EXPECT_EQ(VPV3, I1->getOperand(1));
EXPECT_EQ(0u, VPV1->getNumUsers());
EXPECT_EQ(0u, VPV2->getNumUsers());
EXPECT_EQ(2u, VPV3->getNumUsers());
EXPECT_EQ(I1, *VPV3->user_begin());
// Replace all uses of VPV3 with VPV1.
VPV3->replaceAllUsesWith(VPV1);
EXPECT_EQ(VPV1, I1->getOperand(0));
EXPECT_EQ(VPV1, I1->getOperand(1));
EXPECT_EQ(2u, VPV1->getNumUsers());
EXPECT_EQ(I1, *VPV1->user_begin());
EXPECT_EQ(0u, VPV2->getNumUsers());
EXPECT_EQ(0u, VPV3->getNumUsers());
VPInstruction *I2 = new VPInstruction(0, {VPV1, VPV2});
EXPECT_EQ(3u, VPV1->getNumUsers());
VPV1->replaceAllUsesWith(VPV3);
EXPECT_EQ(3u, VPV3->getNumUsers());
delete I1;
delete I2;
delete VPV1;
delete VPV2;
delete VPV3;
}
TEST(VPInstructionTest, releaseOperandsAtDeletion) {
VPValue *VPV1 = new VPValue();
VPValue *VPV2 = new VPValue();
VPInstruction *I1 = new VPInstruction(0, {VPV1, VPV2});
EXPECT_EQ(1u, VPV1->getNumUsers());
EXPECT_EQ(I1, *VPV1->user_begin());
EXPECT_EQ(1u, VPV2->getNumUsers());
EXPECT_EQ(I1, *VPV2->user_begin());
delete I1;
EXPECT_EQ(0u, VPV1->getNumUsers());
EXPECT_EQ(0u, VPV2->getNumUsers());
delete VPV1;
delete VPV2;
}
TEST(VPBasicBlockTest, getPlan) {
LLVMContext C;
auto ScalarHeader = std::make_unique<BasicBlock *>(BasicBlock::Create(C, ""));
{
VPBasicBlock *VPPH = new VPBasicBlock("ph");
VPBasicBlock *VPBB1 = new VPBasicBlock();
VPBasicBlock *VPBB2 = new VPBasicBlock();
VPBasicBlock *VPBB3 = new VPBasicBlock();
VPBasicBlock *VPBB4 = new VPBasicBlock();
// VPBB1
// / \
// VPBB2 VPBB3
// \ /
// VPBB4
VPBlockUtils::connectBlocks(VPBB1, VPBB2);
VPBlockUtils::connectBlocks(VPBB1, VPBB3);
VPBlockUtils::connectBlocks(VPBB2, VPBB4);
VPBlockUtils::connectBlocks(VPBB3, VPBB4);
auto TC = std::make_unique<VPValue>();
VPIRBasicBlock *ScalarHeaderVPBB = new VPIRBasicBlock(*ScalarHeader);
VPlan Plan(VPPH, &*TC, VPBB1, ScalarHeaderVPBB);
EXPECT_EQ(&Plan, VPBB1->getPlan());
EXPECT_EQ(&Plan, VPBB2->getPlan());
EXPECT_EQ(&Plan, VPBB3->getPlan());
EXPECT_EQ(&Plan, VPBB4->getPlan());
}
{
VPBasicBlock *VPPH = new VPBasicBlock("ph");
// VPBasicBlock is the entry into the VPlan, followed by a region.
VPBasicBlock *R1BB1 = new VPBasicBlock();
VPBasicBlock *R1BB2 = new VPBasicBlock();
VPRegionBlock *R1 = new VPRegionBlock(R1BB1, R1BB2, "R1");
VPBlockUtils::connectBlocks(R1BB1, R1BB2);
VPBasicBlock *VPBB1 = new VPBasicBlock();
VPBlockUtils::connectBlocks(VPBB1, R1);
auto TC = std::make_unique<VPValue>();
VPIRBasicBlock *ScalarHeaderVPBB = new VPIRBasicBlock(*ScalarHeader);
VPlan Plan(VPPH, &*TC, VPBB1, ScalarHeaderVPBB);
EXPECT_EQ(&Plan, VPBB1->getPlan());
EXPECT_EQ(&Plan, R1->getPlan());
EXPECT_EQ(&Plan, R1BB1->getPlan());
EXPECT_EQ(&Plan, R1BB2->getPlan());
}
{
VPBasicBlock *VPPH = new VPBasicBlock("ph");
VPBasicBlock *R1BB1 = new VPBasicBlock();
VPBasicBlock *R1BB2 = new VPBasicBlock();
VPRegionBlock *R1 = new VPRegionBlock(R1BB1, R1BB2, "R1");
VPBlockUtils::connectBlocks(R1BB1, R1BB2);
VPBasicBlock *R2BB1 = new VPBasicBlock();
VPBasicBlock *R2BB2 = new VPBasicBlock();
VPRegionBlock *R2 = new VPRegionBlock(R2BB1, R2BB2, "R2");
VPBlockUtils::connectBlocks(R2BB1, R2BB2);
VPBasicBlock *VPBB1 = new VPBasicBlock();
VPBlockUtils::connectBlocks(VPBB1, R1);
VPBlockUtils::connectBlocks(VPBB1, R2);
VPBasicBlock *VPBB2 = new VPBasicBlock();
VPBlockUtils::connectBlocks(R1, VPBB2);
VPBlockUtils::connectBlocks(R2, VPBB2);
auto TC = std::make_unique<VPValue>();
VPIRBasicBlock *ScalarHeaderVPBB = new VPIRBasicBlock(*ScalarHeader);
VPlan Plan(VPPH, &*TC, VPBB1, ScalarHeaderVPBB);
EXPECT_EQ(&Plan, VPBB1->getPlan());
EXPECT_EQ(&Plan, R1->getPlan());
EXPECT_EQ(&Plan, R1BB1->getPlan());
EXPECT_EQ(&Plan, R1BB2->getPlan());
EXPECT_EQ(&Plan, R2->getPlan());
EXPECT_EQ(&Plan, R2BB1->getPlan());
EXPECT_EQ(&Plan, R2BB2->getPlan());
EXPECT_EQ(&Plan, VPBB2->getPlan());
}
}
TEST(VPBasicBlockTest, TraversingIteratorTest) {
LLVMContext C;
auto ScalarHeader = std::make_unique<BasicBlock *>(BasicBlock::Create(C, ""));
{
// VPBasicBlocks only
// VPBB1
// / \
// VPBB2 VPBB3
// \ /
// VPBB4
//
VPBasicBlock *VPPH = new VPBasicBlock("ph");
VPBasicBlock *VPBB1 = new VPBasicBlock();
VPBasicBlock *VPBB2 = new VPBasicBlock();
VPBasicBlock *VPBB3 = new VPBasicBlock();
VPBasicBlock *VPBB4 = new VPBasicBlock();
VPBlockUtils::connectBlocks(VPBB1, VPBB2);
VPBlockUtils::connectBlocks(VPBB1, VPBB3);
VPBlockUtils::connectBlocks(VPBB2, VPBB4);
VPBlockUtils::connectBlocks(VPBB3, VPBB4);
VPBlockDeepTraversalWrapper<const VPBlockBase *> Start(VPBB1);
SmallVector<const VPBlockBase *> FromIterator(depth_first(Start));
EXPECT_EQ(4u, FromIterator.size());
EXPECT_EQ(VPBB1, FromIterator[0]);
EXPECT_EQ(VPBB2, FromIterator[1]);
// Use Plan to properly clean up created blocks.
auto TC = std::make_unique<VPValue>();
VPIRBasicBlock *ScalarHeaderVPBB = new VPIRBasicBlock(*ScalarHeader);
VPlan Plan(VPPH, &*TC, VPBB1, ScalarHeaderVPBB);
}
{
// 2 consecutive regions.
// VPBB0
// |
// R1 {
// \
// R1BB1
// / \ |--|
// R1BB2 R1BB3 -|
// \ /
// R1BB4
// }
// |
// R2 {
// \
// R2BB1
// |
// R2BB2
//
VPBasicBlock *VPPH = new VPBasicBlock("ph");
VPBasicBlock *VPBB0 = new VPBasicBlock("VPBB0");
VPBasicBlock *R1BB1 = new VPBasicBlock();
VPBasicBlock *R1BB2 = new VPBasicBlock();
VPBasicBlock *R1BB3 = new VPBasicBlock();
VPBasicBlock *R1BB4 = new VPBasicBlock();
VPRegionBlock *R1 = new VPRegionBlock(R1BB1, R1BB4, "R1");
R1BB2->setParent(R1);
R1BB3->setParent(R1);
VPBlockUtils::connectBlocks(VPBB0, R1);
VPBlockUtils::connectBlocks(R1BB1, R1BB2);
VPBlockUtils::connectBlocks(R1BB1, R1BB3);
VPBlockUtils::connectBlocks(R1BB2, R1BB4);
VPBlockUtils::connectBlocks(R1BB3, R1BB4);
// Cycle.
VPBlockUtils::connectBlocks(R1BB3, R1BB3);
VPBasicBlock *R2BB1 = new VPBasicBlock();
VPBasicBlock *R2BB2 = new VPBasicBlock();
VPRegionBlock *R2 = new VPRegionBlock(R2BB1, R2BB2, "R2");
VPBlockUtils::connectBlocks(R2BB1, R2BB2);
VPBlockUtils::connectBlocks(R1, R2);
// Successors of R1.
SmallVector<const VPBlockBase *> FromIterator(
VPAllSuccessorsIterator<VPBlockBase *>(R1),
VPAllSuccessorsIterator<VPBlockBase *>::end(R1));
EXPECT_EQ(1u, FromIterator.size());
EXPECT_EQ(R1BB1, FromIterator[0]);
// Depth-first.
VPBlockDeepTraversalWrapper<VPBlockBase *> Start(R1);
FromIterator.clear();
copy(df_begin(Start), df_end(Start), std::back_inserter(FromIterator));
EXPECT_EQ(8u, FromIterator.size());
EXPECT_EQ(R1, FromIterator[0]);
EXPECT_EQ(R1BB1, FromIterator[1]);
EXPECT_EQ(R1BB2, FromIterator[2]);
EXPECT_EQ(R1BB4, FromIterator[3]);
EXPECT_EQ(R2, FromIterator[4]);
EXPECT_EQ(R2BB1, FromIterator[5]);
EXPECT_EQ(R2BB2, FromIterator[6]);
EXPECT_EQ(R1BB3, FromIterator[7]);
// const VPBasicBlocks only.
FromIterator.clear();
copy(VPBlockUtils::blocksOnly<const VPBasicBlock>(depth_first(Start)),
std::back_inserter(FromIterator));
EXPECT_EQ(6u, FromIterator.size());
EXPECT_EQ(R1BB1, FromIterator[0]);
EXPECT_EQ(R1BB2, FromIterator[1]);
EXPECT_EQ(R1BB4, FromIterator[2]);
EXPECT_EQ(R2BB1, FromIterator[3]);
EXPECT_EQ(R2BB2, FromIterator[4]);
EXPECT_EQ(R1BB3, FromIterator[5]);
// VPRegionBlocks only.
SmallVector<VPRegionBlock *> FromIteratorVPRegion(
VPBlockUtils::blocksOnly<VPRegionBlock>(depth_first(Start)));
EXPECT_EQ(2u, FromIteratorVPRegion.size());
EXPECT_EQ(R1, FromIteratorVPRegion[0]);
EXPECT_EQ(R2, FromIteratorVPRegion[1]);
// Post-order.
FromIterator.clear();
copy(post_order(Start), std::back_inserter(FromIterator));
EXPECT_EQ(8u, FromIterator.size());
EXPECT_EQ(R2BB2, FromIterator[0]);
EXPECT_EQ(R2BB1, FromIterator[1]);
EXPECT_EQ(R2, FromIterator[2]);
EXPECT_EQ(R1BB4, FromIterator[3]);
EXPECT_EQ(R1BB2, FromIterator[4]);
EXPECT_EQ(R1BB3, FromIterator[5]);
EXPECT_EQ(R1BB1, FromIterator[6]);
EXPECT_EQ(R1, FromIterator[7]);
// Use Plan to properly clean up created blocks.
auto TC = std::make_unique<VPValue>();
VPIRBasicBlock *ScalarHeaderVPBB = new VPIRBasicBlock(*ScalarHeader);
VPlan Plan(VPPH, &*TC, VPBB0, ScalarHeaderVPBB);
}
{
// 2 nested regions.
// VPBB1
// |
// R1 {
// R1BB1
// / \
// R2 { |
// \ |
// R2BB1 |
// | \ R1BB2
// R2BB2-| |
// \ |
// R2BB3 |
// } /
// \ /
// R1BB3
// }
// |
// VPBB2
//
VPBasicBlock *VPPH = new VPBasicBlock("ph");
VPBasicBlock *R1BB1 = new VPBasicBlock("R1BB1");
VPBasicBlock *R1BB2 = new VPBasicBlock("R1BB2");
VPBasicBlock *R1BB3 = new VPBasicBlock("R1BB3");
VPRegionBlock *R1 = new VPRegionBlock(R1BB1, R1BB3, "R1");
VPBasicBlock *R2BB1 = new VPBasicBlock("R2BB1");
VPBasicBlock *R2BB2 = new VPBasicBlock("R2BB2");
VPBasicBlock *R2BB3 = new VPBasicBlock("R2BB3");
VPRegionBlock *R2 = new VPRegionBlock(R2BB1, R2BB3, "R2");
R2BB2->setParent(R2);
VPBlockUtils::connectBlocks(R2BB1, R2BB2);
VPBlockUtils::connectBlocks(R2BB2, R2BB1);
VPBlockUtils::connectBlocks(R2BB2, R2BB3);
R2->setParent(R1);
VPBlockUtils::connectBlocks(R1BB1, R2);
R1BB2->setParent(R1);
VPBlockUtils::connectBlocks(R1BB1, R1BB2);
VPBlockUtils::connectBlocks(R1BB2, R1BB3);
VPBlockUtils::connectBlocks(R2, R1BB3);
VPBasicBlock *VPBB1 = new VPBasicBlock("VPBB1");
VPBlockUtils::connectBlocks(VPBB1, R1);
VPBasicBlock *VPBB2 = new VPBasicBlock("VPBB2");
VPBlockUtils::connectBlocks(R1, VPBB2);
// Depth-first.
VPBlockDeepTraversalWrapper<VPBlockBase *> Start(VPBB1);
SmallVector<VPBlockBase *> FromIterator(depth_first(Start));
EXPECT_EQ(10u, FromIterator.size());
EXPECT_EQ(VPBB1, FromIterator[0]);
EXPECT_EQ(R1, FromIterator[1]);
EXPECT_EQ(R1BB1, FromIterator[2]);
EXPECT_EQ(R2, FromIterator[3]);
EXPECT_EQ(R2BB1, FromIterator[4]);
EXPECT_EQ(R2BB2, FromIterator[5]);
EXPECT_EQ(R2BB3, FromIterator[6]);
EXPECT_EQ(R1BB3, FromIterator[7]);
EXPECT_EQ(VPBB2, FromIterator[8]);
EXPECT_EQ(R1BB2, FromIterator[9]);
// Post-order.
FromIterator.clear();
FromIterator.append(po_begin(Start), po_end(Start));
EXPECT_EQ(10u, FromIterator.size());
EXPECT_EQ(VPBB2, FromIterator[0]);
EXPECT_EQ(R1BB3, FromIterator[1]);
EXPECT_EQ(R2BB3, FromIterator[2]);
EXPECT_EQ(R2BB2, FromIterator[3]);
EXPECT_EQ(R2BB1, FromIterator[4]);
EXPECT_EQ(R2, FromIterator[5]);
EXPECT_EQ(R1BB2, FromIterator[6]);
EXPECT_EQ(R1BB1, FromIterator[7]);
EXPECT_EQ(R1, FromIterator[8]);
EXPECT_EQ(VPBB1, FromIterator[9]);
// Use Plan to properly clean up created blocks.
auto TC = std::make_unique<VPValue>();
VPIRBasicBlock *ScalarHeaderVPBB = new VPIRBasicBlock(*ScalarHeader);
VPlan Plan(VPPH, &*TC, VPBB1, ScalarHeaderVPBB);
}
{
// VPBB1
// |
// R1 {
// \
// R2 {
// R2BB1
// |
// R2BB2
// }
//
VPBasicBlock *VPPH = new VPBasicBlock("ph");
VPBasicBlock *R2BB1 = new VPBasicBlock("R2BB1");
VPBasicBlock *R2BB2 = new VPBasicBlock("R2BB2");
VPRegionBlock *R2 = new VPRegionBlock(R2BB1, R2BB2, "R2");
VPBlockUtils::connectBlocks(R2BB1, R2BB2);
VPRegionBlock *R1 = new VPRegionBlock(R2, R2, "R1");
R2->setParent(R1);
VPBasicBlock *VPBB1 = new VPBasicBlock("VPBB1");
VPBlockUtils::connectBlocks(VPBB1, R1);
// Depth-first.
VPBlockDeepTraversalWrapper<VPBlockBase *> Start(VPBB1);
SmallVector<VPBlockBase *> FromIterator(depth_first(Start));
EXPECT_EQ(5u, FromIterator.size());
EXPECT_EQ(VPBB1, FromIterator[0]);
EXPECT_EQ(R1, FromIterator[1]);
EXPECT_EQ(R2, FromIterator[2]);
EXPECT_EQ(R2BB1, FromIterator[3]);
EXPECT_EQ(R2BB2, FromIterator[4]);
// Post-order.
FromIterator.clear();
FromIterator.append(po_begin(Start), po_end(Start));
EXPECT_EQ(5u, FromIterator.size());
EXPECT_EQ(R2BB2, FromIterator[0]);
EXPECT_EQ(R2BB1, FromIterator[1]);
EXPECT_EQ(R2, FromIterator[2]);
EXPECT_EQ(R1, FromIterator[3]);
EXPECT_EQ(VPBB1, FromIterator[4]);
// Use Plan to properly clean up created blocks.
auto TC = std::make_unique<VPValue>();
VPIRBasicBlock *ScalarHeaderVPBB = new VPIRBasicBlock(*ScalarHeader);
VPlan Plan(VPPH, &*TC, VPBB1, ScalarHeaderVPBB);
}
{
// Nested regions with both R3 and R2 being exit nodes without successors.
// The successors of R1 should be used.
//
// VPBB1
// |
// R1 {
// \
// R2 {
// \
// R2BB1
// |
// R3 {
// R3BB1
// }
// }
// |
// VPBB2
//
VPBasicBlock *VPPH = new VPBasicBlock("ph");
VPBasicBlock *R3BB1 = new VPBasicBlock("R3BB1");
VPRegionBlock *R3 = new VPRegionBlock(R3BB1, R3BB1, "R3");
VPBasicBlock *R2BB1 = new VPBasicBlock("R2BB1");
VPRegionBlock *R2 = new VPRegionBlock(R2BB1, R3, "R2");
R3->setParent(R2);
VPBlockUtils::connectBlocks(R2BB1, R3);
VPRegionBlock *R1 = new VPRegionBlock(R2, R2, "R1");
R2->setParent(R1);
VPBasicBlock *VPBB1 = new VPBasicBlock("VPBB1");
VPBasicBlock *VPBB2 = new VPBasicBlock("VPBB2");
VPBlockUtils::connectBlocks(VPBB1, R1);
VPBlockUtils::connectBlocks(R1, VPBB2);
// Depth-first.
VPBlockDeepTraversalWrapper<VPBlockBase *> Start(VPBB1);
SmallVector<VPBlockBase *> FromIterator(depth_first(Start));
EXPECT_EQ(7u, FromIterator.size());
EXPECT_EQ(VPBB1, FromIterator[0]);
EXPECT_EQ(R1, FromIterator[1]);
EXPECT_EQ(R2, FromIterator[2]);
EXPECT_EQ(R2BB1, FromIterator[3]);
EXPECT_EQ(R3, FromIterator[4]);
EXPECT_EQ(R3BB1, FromIterator[5]);
EXPECT_EQ(VPBB2, FromIterator[6]);
SmallVector<VPBlockBase *> FromIteratorVPBB;
copy(VPBlockUtils::blocksOnly<VPBasicBlock>(depth_first(Start)),
std::back_inserter(FromIteratorVPBB));
EXPECT_EQ(VPBB1, FromIteratorVPBB[0]);
EXPECT_EQ(R2BB1, FromIteratorVPBB[1]);
EXPECT_EQ(R3BB1, FromIteratorVPBB[2]);
EXPECT_EQ(VPBB2, FromIteratorVPBB[3]);
// Post-order.
FromIterator.clear();
copy(post_order(Start), std::back_inserter(FromIterator));
EXPECT_EQ(7u, FromIterator.size());
EXPECT_EQ(VPBB2, FromIterator[0]);
EXPECT_EQ(R3BB1, FromIterator[1]);
EXPECT_EQ(R3, FromIterator[2]);
EXPECT_EQ(R2BB1, FromIterator[3]);
EXPECT_EQ(R2, FromIterator[4]);
EXPECT_EQ(R1, FromIterator[5]);
EXPECT_EQ(VPBB1, FromIterator[6]);
// Post-order, const VPRegionBlocks only.
VPBlockDeepTraversalWrapper<const VPBlockBase *> StartConst(VPBB1);
SmallVector<const VPRegionBlock *> FromIteratorVPRegion(
VPBlockUtils::blocksOnly<const VPRegionBlock>(post_order(StartConst)));
EXPECT_EQ(3u, FromIteratorVPRegion.size());
EXPECT_EQ(R3, FromIteratorVPRegion[0]);
EXPECT_EQ(R2, FromIteratorVPRegion[1]);
EXPECT_EQ(R1, FromIteratorVPRegion[2]);
// Post-order, VPBasicBlocks only.
FromIterator.clear();
copy(VPBlockUtils::blocksOnly<VPBasicBlock>(post_order(Start)),
std::back_inserter(FromIterator));
EXPECT_EQ(FromIterator.size(), 4u);
EXPECT_EQ(VPBB2, FromIterator[0]);
EXPECT_EQ(R3BB1, FromIterator[1]);
EXPECT_EQ(R2BB1, FromIterator[2]);
EXPECT_EQ(VPBB1, FromIterator[3]);
// Use Plan to properly clean up created blocks.
auto TC = std::make_unique<VPValue>();
VPIRBasicBlock *ScalarHeaderVPBB = new VPIRBasicBlock(*ScalarHeader);
VPlan Plan(VPPH, &*TC, VPBB1, ScalarHeaderVPBB);
}
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
TEST(VPBasicBlockTest, print) {
VPInstruction *TC = new VPInstruction(Instruction::Add, {});
VPBasicBlock *VPBB0 = new VPBasicBlock("preheader");
VPBB0->appendRecipe(TC);
VPInstruction *I1 = new VPInstruction(Instruction::Add, {});
VPInstruction *I2 = new VPInstruction(Instruction::Sub, {I1});
VPInstruction *I3 = new VPInstruction(Instruction::Br, {I1, I2});
VPBasicBlock *VPBB1 = new VPBasicBlock();
VPBB1->appendRecipe(I1);
VPBB1->appendRecipe(I2);
VPBB1->appendRecipe(I3);
VPBB1->setName("bb1");
VPInstruction *I4 = new VPInstruction(Instruction::Mul, {I2, I1});
VPInstruction *I5 = new VPInstruction(Instruction::Ret, {I4});
VPBasicBlock *VPBB2 = new VPBasicBlock();
VPBB2->appendRecipe(I4);
VPBB2->appendRecipe(I5);
VPBB2->setName("bb2");
VPBlockUtils::connectBlocks(VPBB1, VPBB2);
// Check printing an instruction without associated VPlan.
{
std::string I3Dump;
raw_string_ostream OS(I3Dump);
VPSlotTracker SlotTracker;
I3->print(OS, "", SlotTracker);
EXPECT_EQ("EMIT br <badref>, <badref>", I3Dump);
}
LLVMContext C;
auto ScalarHeader = std::make_unique<BasicBlock *>(BasicBlock::Create(C, ""));
VPIRBasicBlock *ScalarHeaderVPBB = new VPIRBasicBlock(*ScalarHeader);
VPlan Plan(VPBB0, TC, VPBB1, ScalarHeaderVPBB);
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\nvp\<%1\> = original trip-count\n"]
node [shape=rect, fontname=Courier, fontsize=30]
edge [fontname=Courier, fontsize=30]
compound=true
N0 [label =
"preheader:\l" +
" EMIT vp\<%1\> = add\l" +
"No successors\l"
]
N1 [label =
"bb1:\l" +
" EMIT vp\<%2\> = add\l" +
" EMIT vp\<%3\> = sub vp\<%2\>\l" +
" EMIT br vp\<%2\>, vp\<%3\>\l" +
"Successor(s): bb2\l"
]
N1 -> N2 [ label=""]
N2 [label =
"bb2:\l" +
" EMIT vp\<%5\> = mul vp\<%3\>, vp\<%2\>\l" +
" EMIT ret vp\<%5\>\l" +
"No successors\l"
]
}
)";
EXPECT_EQ(ExpectedStr, FullDump);
const char *ExpectedBlock1Str = R"(bb1:
EMIT vp<%2> = add
EMIT vp<%3> = sub vp<%2>
EMIT br vp<%2>, vp<%3>
Successor(s): bb2
)";
std::string Block1Dump;
raw_string_ostream OS1(Block1Dump);
VPBB1->print(OS1);
EXPECT_EQ(ExpectedBlock1Str, Block1Dump);
// Ensure that numbering is good when dumping the second block in isolation.
const char *ExpectedBlock2Str = R"(bb2:
EMIT vp<%5> = mul vp<%3>, vp<%2>
EMIT ret vp<%5>
No successors
)";
std::string Block2Dump;
raw_string_ostream OS2(Block2Dump);
VPBB2->print(OS2);
EXPECT_EQ(ExpectedBlock2Str, Block2Dump);
{
std::string I3Dump;
raw_string_ostream OS(I3Dump);
VPSlotTracker SlotTracker(&Plan);
I3->print(OS, "", SlotTracker);
EXPECT_EQ("EMIT br vp<%2>, vp<%3>", I3Dump);
}
{
std::string I4Dump;
raw_string_ostream OS(I4Dump);
OS << *I4;
EXPECT_EQ("EMIT vp<%5> = mul vp<%3>, vp<%2>", I4Dump);
}
}
TEST(VPBasicBlockTest, printPlanWithVFsAndUFs) {
VPInstruction *TC = new VPInstruction(Instruction::Sub, {});
VPBasicBlock *VPBB0 = new VPBasicBlock("preheader");
VPBB0->appendRecipe(TC);
VPInstruction *I1 = new VPInstruction(Instruction::Add, {});
VPBasicBlock *VPBB1 = new VPBasicBlock();
VPBB1->appendRecipe(I1);
VPBB1->setName("bb1");
LLVMContext C;
auto ScalarHeader = std::make_unique<BasicBlock *>(BasicBlock::Create(C, ""));
VPIRBasicBlock *ScalarHeaderVPBB = new VPIRBasicBlock(*ScalarHeader);
VPlan Plan(VPBB0, TC, VPBB1, ScalarHeaderVPBB);
Plan.setName("TestPlan");
Plan.addVF(ElementCount::getFixed(4));
{
std::string FullDump;
raw_string_ostream OS(FullDump);
Plan.print(OS);
const char *ExpectedStr = R"(VPlan 'TestPlan for VF={4},UF>=1' {
vp<%1> = original trip-count
preheader:
EMIT vp<%1> = sub
No successors
bb1:
EMIT vp<%2> = add
No successors
}
)";
EXPECT_EQ(ExpectedStr, FullDump);
}
{
Plan.addVF(ElementCount::getScalable(8));
std::string FullDump;
raw_string_ostream OS(FullDump);
Plan.print(OS);
const char *ExpectedStr = R"(VPlan 'TestPlan for VF={4,vscale x 8},UF>=1' {
vp<%1> = original trip-count
preheader:
EMIT vp<%1> = sub
No successors
bb1:
EMIT vp<%2> = add
No successors
}
)";
EXPECT_EQ(ExpectedStr, FullDump);
}
{
Plan.setUF(4);
std::string FullDump;
raw_string_ostream OS(FullDump);
Plan.print(OS);
const char *ExpectedStr = R"(VPlan 'TestPlan for VF={4,vscale x 8},UF={4}' {
vp<%1> = original trip-count
preheader:
EMIT vp<%1> = sub
No successors
bb1:
EMIT vp<%2> = add
No successors
}
)";
EXPECT_EQ(ExpectedStr, FullDump);
}
}
#endif
TEST(VPRecipeTest, CastVPInstructionToVPUser) {
VPValue Op1;
VPValue Op2;
VPInstruction Recipe(Instruction::Add, {&Op1, &Op2});
EXPECT_TRUE(isa<VPUser>(&Recipe));
VPRecipeBase *BaseR = &Recipe;
EXPECT_TRUE(isa<VPUser>(BaseR));
EXPECT_EQ(&Recipe, BaseR);
}
TEST(VPRecipeTest, CastVPWidenRecipeToVPUser) {
LLVMContext C;
IntegerType *Int32 = IntegerType::get(C, 32);
auto *AI = BinaryOperator::CreateAdd(PoisonValue::get(Int32),
PoisonValue::get(Int32));
VPValue Op1;
VPValue Op2;
SmallVector<VPValue *, 2> Args;
Args.push_back(&Op1);
Args.push_back(&Op1);
VPWidenRecipe WidenR(*AI, make_range(Args.begin(), Args.end()));
EXPECT_TRUE(isa<VPUser>(&WidenR));
VPRecipeBase *WidenRBase = &WidenR;
EXPECT_TRUE(isa<VPUser>(WidenRBase));
EXPECT_EQ(&WidenR, WidenRBase);
delete AI;
}
TEST(VPRecipeTest, CastVPWidenCallRecipeToVPUserAndVPDef) {
LLVMContext C;
IntegerType *Int32 = IntegerType::get(C, 32);
FunctionType *FTy = FunctionType::get(Int32, false);
Function *Fn = Function::Create(FTy, GlobalValue::ExternalLinkage, 0);
auto *Call = CallInst::Create(FTy, Fn);
VPValue Op1;
VPValue Op2;
VPValue CalledFn(Call->getCalledFunction());
SmallVector<VPValue *, 2> Args;
Args.push_back(&Op1);
Args.push_back(&Op2);
Args.push_back(&CalledFn);
VPWidenCallRecipe Recipe(Call, Fn, Args);
EXPECT_TRUE(isa<VPUser>(&Recipe));
VPRecipeBase *BaseR = &Recipe;
EXPECT_TRUE(isa<VPUser>(BaseR));
EXPECT_EQ(&Recipe, BaseR);
VPValue *VPV = &Recipe;
EXPECT_TRUE(VPV->getDefiningRecipe());
EXPECT_EQ(&Recipe, VPV->getDefiningRecipe());
delete Call;
delete Fn;
}
TEST(VPRecipeTest, CastVPWidenSelectRecipeToVPUserAndVPDef) {
LLVMContext C;
IntegerType *Int1 = IntegerType::get(C, 1);
IntegerType *Int32 = IntegerType::get(C, 32);
auto *SelectI = SelectInst::Create(
PoisonValue::get(Int1), PoisonValue::get(Int32), PoisonValue::get(Int32));
VPValue Op1;
VPValue Op2;
VPValue Op3;
SmallVector<VPValue *, 4> Args;
Args.push_back(&Op1);
Args.push_back(&Op2);
Args.push_back(&Op3);
VPWidenSelectRecipe WidenSelectR(*SelectI,
make_range(Args.begin(), Args.end()));
EXPECT_TRUE(isa<VPUser>(&WidenSelectR));
VPRecipeBase *BaseR = &WidenSelectR;
EXPECT_TRUE(isa<VPUser>(BaseR));
EXPECT_EQ(&WidenSelectR, BaseR);
VPValue *VPV = &WidenSelectR;
EXPECT_TRUE(isa<VPRecipeBase>(VPV->getDefiningRecipe()));
EXPECT_EQ(&WidenSelectR, VPV->getDefiningRecipe());
delete SelectI;
}
TEST(VPRecipeTest, CastVPWidenGEPRecipeToVPUserAndVPDef) {
LLVMContext C;
IntegerType *Int32 = IntegerType::get(C, 32);
PointerType *Int32Ptr = PointerType::get(Int32, 0);
auto *GEP = GetElementPtrInst::Create(Int32, PoisonValue::get(Int32Ptr),
PoisonValue::get(Int32));
VPValue Op1;
VPValue Op2;
SmallVector<VPValue *, 4> Args;
Args.push_back(&Op1);
Args.push_back(&Op2);
VPWidenGEPRecipe Recipe(GEP, make_range(Args.begin(), Args.end()));
EXPECT_TRUE(isa<VPUser>(&Recipe));
VPRecipeBase *BaseR = &Recipe;
EXPECT_TRUE(isa<VPUser>(BaseR));
EXPECT_EQ(&Recipe, BaseR);
VPValue *VPV = &Recipe;
EXPECT_TRUE(isa<VPRecipeBase>(VPV->getDefiningRecipe()));
EXPECT_EQ(&Recipe, VPV->getDefiningRecipe());
delete GEP;
}
TEST(VPRecipeTest, CastVPBlendRecipeToVPUser) {
LLVMContext C;
IntegerType *Int32 = IntegerType::get(C, 32);
auto *Phi = PHINode::Create(Int32, 1);
VPValue I1;
VPValue I2;
VPValue M2;
SmallVector<VPValue *, 4> Args;
Args.push_back(&I1);
Args.push_back(&I2);
Args.push_back(&M2);
VPBlendRecipe Recipe(Phi, Args);
EXPECT_TRUE(isa<VPUser>(&Recipe));
VPRecipeBase *BaseR = &Recipe;
EXPECT_TRUE(isa<VPUser>(BaseR));
delete Phi;
}
TEST(VPRecipeTest, CastVPInterleaveRecipeToVPUser) {
LLVMContext C;
VPValue Addr;
VPValue Mask;
InterleaveGroup<Instruction> IG(4, false, Align(4));
VPInterleaveRecipe Recipe(&IG, &Addr, {}, &Mask, false);
EXPECT_TRUE(isa<VPUser>(&Recipe));
VPRecipeBase *BaseR = &Recipe;
EXPECT_TRUE(isa<VPUser>(BaseR));
EXPECT_EQ(&Recipe, BaseR);
}
TEST(VPRecipeTest, CastVPReplicateRecipeToVPUser) {
LLVMContext C;
VPValue Op1;
VPValue Op2;
SmallVector<VPValue *, 4> Args;
Args.push_back(&Op1);
Args.push_back(&Op2);
IntegerType *Int32 = IntegerType::get(C, 32);
FunctionType *FTy = FunctionType::get(Int32, false);
auto *Call = CallInst::Create(FTy, PoisonValue::get(FTy));
VPReplicateRecipe Recipe(Call, make_range(Args.begin(), Args.end()), true);
EXPECT_TRUE(isa<VPUser>(&Recipe));
VPRecipeBase *BaseR = &Recipe;
EXPECT_TRUE(isa<VPUser>(BaseR));
delete Call;
}
TEST(VPRecipeTest, CastVPBranchOnMaskRecipeToVPUser) {
LLVMContext C;
VPValue Mask;
VPBranchOnMaskRecipe Recipe(&Mask);
EXPECT_TRUE(isa<VPUser>(&Recipe));
VPRecipeBase *BaseR = &Recipe;
EXPECT_TRUE(isa<VPUser>(BaseR));
EXPECT_EQ(&Recipe, BaseR);
}
TEST(VPRecipeTest, CastVPWidenMemoryRecipeToVPUserAndVPDef) {
LLVMContext C;
IntegerType *Int32 = IntegerType::get(C, 32);
PointerType *Int32Ptr = PointerType::get(Int32, 0);
auto *Load =
new LoadInst(Int32, PoisonValue::get(Int32Ptr), "", false, Align(1));
VPValue Addr;
VPValue Mask;
VPWidenLoadRecipe Recipe(*Load, &Addr, &Mask, true, false, {});
EXPECT_TRUE(isa<VPUser>(&Recipe));
VPRecipeBase *BaseR = &Recipe;
EXPECT_TRUE(isa<VPUser>(BaseR));
EXPECT_EQ(&Recipe, BaseR);
VPValue *VPV = Recipe.getVPSingleValue();
EXPECT_TRUE(isa<VPRecipeBase>(VPV->getDefiningRecipe()));
EXPECT_EQ(&Recipe, VPV->getDefiningRecipe());
delete Load;
}
TEST(VPRecipeTest, MayHaveSideEffectsAndMayReadWriteMemory) {
LLVMContext C;
IntegerType *Int1 = IntegerType::get(C, 1);
IntegerType *Int32 = IntegerType::get(C, 32);
PointerType *Int32Ptr = PointerType::get(Int32, 0);
{
auto *AI = BinaryOperator::CreateAdd(PoisonValue::get(Int32),
PoisonValue::get(Int32));
VPValue Op1;
VPValue Op2;
SmallVector<VPValue *, 2> Args;
Args.push_back(&Op1);
Args.push_back(&Op1);
VPWidenRecipe Recipe(*AI, make_range(Args.begin(), Args.end()));
EXPECT_FALSE(Recipe.mayHaveSideEffects());
EXPECT_FALSE(Recipe.mayReadFromMemory());
EXPECT_FALSE(Recipe.mayWriteToMemory());
EXPECT_FALSE(Recipe.mayReadOrWriteMemory());
delete AI;
}
{
auto *SelectI =
SelectInst::Create(PoisonValue::get(Int1), PoisonValue::get(Int32),
PoisonValue::get(Int32));
VPValue Op1;
VPValue Op2;
VPValue Op3;
SmallVector<VPValue *, 4> Args;
Args.push_back(&Op1);
Args.push_back(&Op2);
Args.push_back(&Op3);
VPWidenSelectRecipe Recipe(*SelectI, make_range(Args.begin(), Args.end()));
EXPECT_FALSE(Recipe.mayHaveSideEffects());
EXPECT_FALSE(Recipe.mayReadFromMemory());
EXPECT_FALSE(Recipe.mayWriteToMemory());
EXPECT_FALSE(Recipe.mayReadOrWriteMemory());
delete SelectI;
}
{
auto *GEP = GetElementPtrInst::Create(Int32, PoisonValue::get(Int32Ptr),
PoisonValue::get(Int32));
VPValue Op1;
VPValue Op2;
SmallVector<VPValue *, 4> Args;
Args.push_back(&Op1);
Args.push_back(&Op2);
VPWidenGEPRecipe Recipe(GEP, make_range(Args.begin(), Args.end()));
EXPECT_FALSE(Recipe.mayHaveSideEffects());
EXPECT_FALSE(Recipe.mayReadFromMemory());
EXPECT_FALSE(Recipe.mayWriteToMemory());
EXPECT_FALSE(Recipe.mayReadOrWriteMemory());
delete GEP;
}
{
VPValue Mask;
VPBranchOnMaskRecipe Recipe(&Mask);
EXPECT_TRUE(Recipe.mayHaveSideEffects());
EXPECT_FALSE(Recipe.mayReadFromMemory());
EXPECT_FALSE(Recipe.mayWriteToMemory());
EXPECT_FALSE(Recipe.mayReadOrWriteMemory());
}
{
VPValue ChainOp;
VPValue VecOp;
VPValue CondOp;
VPReductionRecipe Recipe(RecurrenceDescriptor(), nullptr, &ChainOp, &CondOp,
&VecOp, false);
EXPECT_FALSE(Recipe.mayHaveSideEffects());
EXPECT_FALSE(Recipe.mayReadFromMemory());
EXPECT_FALSE(Recipe.mayWriteToMemory());
EXPECT_FALSE(Recipe.mayReadOrWriteMemory());
}
{
VPValue ChainOp;
VPValue VecOp;
VPValue CondOp;
VPReductionRecipe Recipe(RecurrenceDescriptor(), nullptr, &ChainOp, &CondOp,
&VecOp, false);
VPValue EVL;
VPReductionEVLRecipe EVLRecipe(Recipe, EVL, &CondOp);
EXPECT_FALSE(EVLRecipe.mayHaveSideEffects());
EXPECT_FALSE(EVLRecipe.mayReadFromMemory());
EXPECT_FALSE(EVLRecipe.mayWriteToMemory());
EXPECT_FALSE(EVLRecipe.mayReadOrWriteMemory());
}
{
auto *Load =
new LoadInst(Int32, PoisonValue::get(Int32Ptr), "", false, Align(1));
VPValue Addr;
VPValue Mask;
VPWidenLoadRecipe Recipe(*Load, &Addr, &Mask, true, false, {});
EXPECT_FALSE(Recipe.mayHaveSideEffects());
EXPECT_TRUE(Recipe.mayReadFromMemory());
EXPECT_FALSE(Recipe.mayWriteToMemory());
EXPECT_TRUE(Recipe.mayReadOrWriteMemory());
delete Load;
}
{
auto *Store = new StoreInst(PoisonValue::get(Int32),
PoisonValue::get(Int32Ptr), false, Align(1));
VPValue Addr;
VPValue Mask;
VPValue StoredV;
VPWidenStoreRecipe Recipe(*Store, &Addr, &StoredV, &Mask, false, false, {});
EXPECT_TRUE(Recipe.mayHaveSideEffects());
EXPECT_FALSE(Recipe.mayReadFromMemory());
EXPECT_TRUE(Recipe.mayWriteToMemory());
EXPECT_TRUE(Recipe.mayReadOrWriteMemory());
delete Store;
}
{
FunctionType *FTy = FunctionType::get(Int32, false);
Function *Fn = Function::Create(FTy, GlobalValue::ExternalLinkage, 0);
auto *Call = CallInst::Create(FTy, Fn);
VPValue Op1;
VPValue Op2;
VPValue CalledFn(Call->getCalledFunction());
SmallVector<VPValue *, 3> Args;
Args.push_back(&Op1);
Args.push_back(&Op2);
Args.push_back(&CalledFn);
VPWidenCallRecipe Recipe(Call, Fn, Args);
EXPECT_TRUE(Recipe.mayHaveSideEffects());
EXPECT_TRUE(Recipe.mayReadFromMemory());
EXPECT_TRUE(Recipe.mayWriteToMemory());
EXPECT_TRUE(Recipe.mayReadOrWriteMemory());
delete Call;
delete Fn;
}
{
// Test for a call to a function without side-effects.
LLVMContext C;
Module M("", C);
Function *TheFn =
Intrinsic::getOrInsertDeclaration(&M, Intrinsic::thread_pointer);
auto *Call = CallInst::Create(TheFn->getFunctionType(), TheFn);
VPValue Op1;
VPValue Op2;
VPValue CalledFn(TheFn);
SmallVector<VPValue *, 3> Args;
Args.push_back(&Op1);
Args.push_back(&Op2);
Args.push_back(&CalledFn);
VPWidenCallRecipe Recipe(Call, TheFn, Args);
EXPECT_FALSE(Recipe.mayHaveSideEffects());
EXPECT_FALSE(Recipe.mayReadFromMemory());
EXPECT_FALSE(Recipe.mayWriteToMemory());
EXPECT_FALSE(Recipe.mayReadOrWriteMemory());
delete Call;
}
{
VPValue Op1;
VPValue Op2;
InductionDescriptor IndDesc;
VPScalarIVStepsRecipe Recipe(IndDesc, &Op1, &Op2);
EXPECT_FALSE(Recipe.mayHaveSideEffects());
EXPECT_FALSE(Recipe.mayReadFromMemory());
EXPECT_FALSE(Recipe.mayWriteToMemory());
EXPECT_FALSE(Recipe.mayReadOrWriteMemory());
}
// The initial implementation is conservative with respect to VPInstructions.
{
VPValue Op1;
VPValue Op2;
VPInstruction VPInst(Instruction::Add, {&Op1, &Op2});
VPRecipeBase &Recipe = VPInst;
EXPECT_FALSE(Recipe.mayHaveSideEffects());
EXPECT_TRUE(Recipe.mayReadFromMemory());
EXPECT_FALSE(Recipe.mayWriteToMemory());
EXPECT_TRUE(Recipe.mayReadOrWriteMemory());
}
{
VPValue Op1;
VPPredInstPHIRecipe Recipe(&Op1);
EXPECT_FALSE(Recipe.mayHaveSideEffects());
EXPECT_FALSE(Recipe.mayReadFromMemory());
EXPECT_FALSE(Recipe.mayWriteToMemory());
EXPECT_FALSE(Recipe.mayReadOrWriteMemory());
}
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
TEST(VPRecipeTest, dumpRecipeInPlan) {
VPBasicBlock *VPBB0 = new VPBasicBlock("preheader");
VPBasicBlock *VPBB1 = new VPBasicBlock();
LLVMContext C;
auto ScalarHeader = std::make_unique<BasicBlock *>(BasicBlock::Create(C, ""));
VPIRBasicBlock *ScalarHeaderVPBB = new VPIRBasicBlock(*ScalarHeader);
VPlan Plan(VPBB0, VPBB1, ScalarHeaderVPBB);
IntegerType *Int32 = IntegerType::get(C, 32);
auto *AI = BinaryOperator::CreateAdd(PoisonValue::get(Int32),
PoisonValue::get(Int32));
AI->setName("a");
SmallVector<VPValue *, 2> Args;
VPValue *ExtVPV1 = Plan.getOrAddLiveIn(ConstantInt::get(Int32, 1));
VPValue *ExtVPV2 = Plan.getOrAddLiveIn(ConstantInt::get(Int32, 2));
Args.push_back(ExtVPV1);
Args.push_back(ExtVPV2);
VPWidenRecipe *WidenR =
new VPWidenRecipe(*AI, make_range(Args.begin(), Args.end()));
VPBB1->appendRecipe(WidenR);
{
// Use EXPECT_EXIT to capture stderr and compare against expected output.
//
// Test VPValue::dump().
VPValue *VPV = WidenR;
EXPECT_EXIT(
{
VPV->dump();
exit(0);
},
testing::ExitedWithCode(0), "WIDEN ir<%a> = add ir<1>, ir<2>");
VPDef *Def = WidenR;
EXPECT_EXIT(
{
Def->dump();
exit(0);
},
testing::ExitedWithCode(0), "WIDEN ir<%a> = add ir<1>, ir<2>");
EXPECT_EXIT(
{
WidenR->dump();
exit(0);
},
testing::ExitedWithCode(0), "WIDEN ir<%a> = add ir<1>, ir<2>");
// Test VPRecipeBase::dump().
VPRecipeBase *R = WidenR;
EXPECT_EXIT(
{
R->dump();
exit(0);
},
testing::ExitedWithCode(0), "WIDEN ir<%a> = add ir<1>, ir<2>");
// Test VPDef::dump().
VPDef *D = WidenR;
EXPECT_EXIT(
{
D->dump();
exit(0);
},
testing::ExitedWithCode(0), "WIDEN ir<%a> = add ir<1>, ir<2>");
}
delete AI;
}
TEST(VPRecipeTest, dumpRecipeUnnamedVPValuesInPlan) {
VPBasicBlock *VPBB0 = new VPBasicBlock("preheader");
VPBasicBlock *VPBB1 = new VPBasicBlock();
LLVMContext C;
auto ScalarHeader = std::make_unique<BasicBlock *>(BasicBlock::Create(C, ""));
VPIRBasicBlock *ScalarHeaderVPBB = new VPIRBasicBlock(*ScalarHeader);
VPlan Plan(VPBB0, VPBB1, ScalarHeaderVPBB);
IntegerType *Int32 = IntegerType::get(C, 32);
auto *AI = BinaryOperator::CreateAdd(PoisonValue::get(Int32),
PoisonValue::get(Int32));
AI->setName("a");
SmallVector<VPValue *, 2> Args;
VPValue *ExtVPV1 = Plan.getOrAddLiveIn(ConstantInt::get(Int32, 1));
VPValue *ExtVPV2 = Plan.getOrAddLiveIn(AI);
Args.push_back(ExtVPV1);
Args.push_back(ExtVPV2);
VPInstruction *I1 = new VPInstruction(Instruction::Add, {ExtVPV1, ExtVPV2});
VPInstruction *I2 = new VPInstruction(Instruction::Mul, {I1, I1});
VPBB1->appendRecipe(I1);
VPBB1->appendRecipe(I2);
// Check printing I1.
{
// Use EXPECT_EXIT to capture stderr and compare against expected output.
//
// Test VPValue::dump().
VPValue *VPV = I1;
EXPECT_EXIT(
{
VPV->dump();
exit(0);
},
testing::ExitedWithCode(0), "EMIT vp<%1> = add ir<1>, ir<%a>");
// Test VPRecipeBase::dump().
VPRecipeBase *R = I1;
EXPECT_EXIT(
{
R->dump();
exit(0);
},
testing::ExitedWithCode(0), "EMIT vp<%1> = add ir<1>, ir<%a>");
// Test VPDef::dump().
VPDef *D = I1;
EXPECT_EXIT(
{
D->dump();
exit(0);
},
testing::ExitedWithCode(0), "EMIT vp<%1> = add ir<1>, ir<%a>");
}
// Check printing I2.
{
// Use EXPECT_EXIT to capture stderr and compare against expected output.
//
// Test VPValue::dump().
VPValue *VPV = I2;
EXPECT_EXIT(
{
VPV->dump();
exit(0);
},
testing::ExitedWithCode(0), "EMIT vp<%2> = mul vp<%1>, vp<%1>");
// Test VPRecipeBase::dump().
VPRecipeBase *R = I2;
EXPECT_EXIT(
{
R->dump();
exit(0);
},
testing::ExitedWithCode(0), "EMIT vp<%2> = mul vp<%1>, vp<%1>");
// Test VPDef::dump().
VPDef *D = I2;
EXPECT_EXIT(
{
D->dump();
exit(0);
},
testing::ExitedWithCode(0), "EMIT vp<%2> = mul vp<%1>, vp<%1>");
}
delete AI;
}
TEST(VPRecipeTest, dumpRecipeUnnamedVPValuesNotInPlanOrBlock) {
LLVMContext C;
IntegerType *Int32 = IntegerType::get(C, 32);
auto *AI = BinaryOperator::CreateAdd(PoisonValue::get(Int32),
PoisonValue::get(Int32));
AI->setName("a");
VPValue *ExtVPV1 = new VPValue(ConstantInt::get(Int32, 1));
VPValue *ExtVPV2 = new VPValue(AI);
VPInstruction *I1 = new VPInstruction(Instruction::Add, {ExtVPV1, ExtVPV2});
VPInstruction *I2 = new VPInstruction(Instruction::Mul, {I1, I1});
// Check printing I1.
{
// Use EXPECT_EXIT to capture stderr and compare against expected output.
//
// Test VPValue::dump().
VPValue *VPV = I1;
EXPECT_EXIT(
{
VPV->dump();
exit(0);
},
testing::ExitedWithCode(0), "EMIT <badref> = add ir<1>, ir<%a>");
// Test VPRecipeBase::dump().
VPRecipeBase *R = I1;
EXPECT_EXIT(
{
R->dump();
exit(0);
},
testing::ExitedWithCode(0), "EMIT <badref> = add ir<1>, ir<%a>");
// Test VPDef::dump().
VPDef *D = I1;
EXPECT_EXIT(
{
D->dump();
exit(0);
},
testing::ExitedWithCode(0), "EMIT <badref> = add ir<1>, ir<%a>");
}
// Check printing I2.
{
// Use EXPECT_EXIT to capture stderr and compare against expected output.
//
// Test VPValue::dump().
VPValue *VPV = I2;
EXPECT_EXIT(
{
VPV->dump();
exit(0);
},
testing::ExitedWithCode(0), "EMIT <badref> = mul <badref>, <badref>");
// Test VPRecipeBase::dump().
VPRecipeBase *R = I2;
EXPECT_EXIT(
{
R->dump();
exit(0);
},
testing::ExitedWithCode(0), "EMIT <badref> = mul <badref>, <badref>");
// Test VPDef::dump().
VPDef *D = I2;
EXPECT_EXIT(
{
D->dump();
exit(0);
},
testing::ExitedWithCode(0), "EMIT <badref> = mul <badref>, <badref>");
}
delete I2;
delete I1;
delete ExtVPV2;
delete ExtVPV1;
delete AI;
}
#endif
TEST(VPRecipeTest, CastVPReductionRecipeToVPUser) {
LLVMContext C;
VPValue ChainOp;
VPValue VecOp;
VPValue CondOp;
VPReductionRecipe Recipe(RecurrenceDescriptor(), nullptr, &ChainOp, &CondOp,
&VecOp, false);
EXPECT_TRUE(isa<VPUser>(&Recipe));
VPRecipeBase *BaseR = &Recipe;
EXPECT_TRUE(isa<VPUser>(BaseR));
}
TEST(VPRecipeTest, CastVPReductionEVLRecipeToVPUser) {
LLVMContext C;
VPValue ChainOp;
VPValue VecOp;
VPValue CondOp;
VPReductionRecipe Recipe(RecurrenceDescriptor(), nullptr, &ChainOp, &CondOp,
&VecOp, false);
VPValue EVL;
VPReductionEVLRecipe EVLRecipe(Recipe, EVL, &CondOp);
EXPECT_TRUE(isa<VPUser>(&EVLRecipe));
VPRecipeBase *BaseR = &EVLRecipe;
EXPECT_TRUE(isa<VPUser>(BaseR));
}
struct VPDoubleValueDef : public VPRecipeBase {
VPDoubleValueDef(ArrayRef<VPValue *> Operands) : VPRecipeBase(99, Operands) {
new VPValue(nullptr, this);
new VPValue(nullptr, this);
}
VPRecipeBase *clone() override { return nullptr; }
void execute(struct VPTransformState &State) override {}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
void print(raw_ostream &O, const Twine &Indent,
VPSlotTracker &SlotTracker) const override {}
#endif
};
TEST(VPDoubleValueDefTest, traverseUseLists) {
// Check that the def-use chains of a multi-def can be traversed in both
// directions.
// Create a new VPDef which defines 2 values and has 2 operands.
VPInstruction Op0(20, {});
VPInstruction Op1(30, {});
VPDoubleValueDef DoubleValueDef({&Op0, &Op1});
// Create a new users of the defined values.
VPInstruction I1(
1, {DoubleValueDef.getVPValue(0), DoubleValueDef.getVPValue(1)});
VPInstruction I2(2, {DoubleValueDef.getVPValue(0)});
VPInstruction I3(3, {DoubleValueDef.getVPValue(1)});
// Check operands of the VPDef (traversing upwards).
SmallVector<VPValue *, 4> DoubleOperands(DoubleValueDef.op_begin(),
DoubleValueDef.op_end());
EXPECT_EQ(2u, DoubleOperands.size());
EXPECT_EQ(&Op0, DoubleOperands[0]);
EXPECT_EQ(&Op1, DoubleOperands[1]);
// Check users of the defined values (traversing downwards).
SmallVector<VPUser *, 4> DoubleValueDefV0Users(
DoubleValueDef.getVPValue(0)->user_begin(),
DoubleValueDef.getVPValue(0)->user_end());
EXPECT_EQ(2u, DoubleValueDefV0Users.size());
EXPECT_EQ(&I1, DoubleValueDefV0Users[0]);
EXPECT_EQ(&I2, DoubleValueDefV0Users[1]);
SmallVector<VPUser *, 4> DoubleValueDefV1Users(
DoubleValueDef.getVPValue(1)->user_begin(),
DoubleValueDef.getVPValue(1)->user_end());
EXPECT_EQ(2u, DoubleValueDefV1Users.size());
EXPECT_EQ(&I1, DoubleValueDefV1Users[0]);
EXPECT_EQ(&I3, DoubleValueDefV1Users[1]);
// Now check that we can get the right VPDef for each defined value.
EXPECT_EQ(&DoubleValueDef, I1.getOperand(0)->getDefiningRecipe());
EXPECT_EQ(&DoubleValueDef, I1.getOperand(1)->getDefiningRecipe());
EXPECT_EQ(&DoubleValueDef, I2.getOperand(0)->getDefiningRecipe());
EXPECT_EQ(&DoubleValueDef, I3.getOperand(0)->getDefiningRecipe());
}
TEST(VPRecipeTest, CastToVPSingleDefRecipe) {
VPValue Start;
VPEVLBasedIVPHIRecipe R(&Start, {});
VPRecipeBase *B = &R;
EXPECT_TRUE(isa<VPSingleDefRecipe>(B));
// TODO: check other VPSingleDefRecipes.
}
} // namespace
} // namespace llvm