|
@@ -0,0 +1,899 @@
|
|
|
+//===- llvm/unittest/Transforms/Vectorize/VPlanSlpTest.cpp ---------------===//
|
|
|
+//
|
|
|
+// The LLVM Compiler Infrastructure
|
|
|
+//
|
|
|
+// This file is distributed under the University of Illinois Open Source
|
|
|
+// License. See LICENSE.TXT for details.
|
|
|
+//
|
|
|
+//===----------------------------------------------------------------------===//
|
|
|
+
|
|
|
+#include "../lib/Transforms/Vectorize/VPlan.h"
|
|
|
+#include "../lib/Transforms/Vectorize/VPlanHCFGBuilder.h"
|
|
|
+#include "../lib/Transforms/Vectorize/VPlanHCFGTransforms.h"
|
|
|
+#include "VPlanTestBase.h"
|
|
|
+#include "llvm/Analysis/VectorUtils.h"
|
|
|
+#include "gtest/gtest.h"
|
|
|
+
|
|
|
+namespace llvm {
|
|
|
+namespace {
|
|
|
+
|
|
|
+class VPlanSlpTest : public VPlanTestBase {
|
|
|
+protected:
|
|
|
+ TargetLibraryInfoImpl TLII;
|
|
|
+ TargetLibraryInfo TLI;
|
|
|
+ DataLayout DL;
|
|
|
+
|
|
|
+ std::unique_ptr<AssumptionCache> AC;
|
|
|
+ std::unique_ptr<ScalarEvolution> SE;
|
|
|
+ std::unique_ptr<AAResults> AARes;
|
|
|
+ std::unique_ptr<BasicAAResult> BasicAA;
|
|
|
+ std::unique_ptr<LoopAccessInfo> LAI;
|
|
|
+ std::unique_ptr<PredicatedScalarEvolution> PSE;
|
|
|
+ std::unique_ptr<InterleavedAccessInfo> IAI;
|
|
|
+
|
|
|
+ VPlanSlpTest()
|
|
|
+ : TLII(), TLI(TLII),
|
|
|
+ DL("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-"
|
|
|
+ "f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:"
|
|
|
+ "16:32:64-S128") {}
|
|
|
+
|
|
|
+ VPInterleavedAccessInfo getInterleavedAccessInfo(Function &F, Loop *L,
|
|
|
+ VPlan &Plan) {
|
|
|
+ AC.reset(new AssumptionCache(F));
|
|
|
+ SE.reset(new ScalarEvolution(F, TLI, *AC, *DT, *LI));
|
|
|
+ BasicAA.reset(new BasicAAResult(DL, F, TLI, *AC, &*DT, &*LI));
|
|
|
+ AARes.reset(new AAResults(TLI));
|
|
|
+ AARes->addAAResult(*BasicAA);
|
|
|
+ PSE.reset(new PredicatedScalarEvolution(*SE, *L));
|
|
|
+ LAI.reset(new LoopAccessInfo(L, &*SE, &TLI, &*AARes, &*DT, &*LI));
|
|
|
+ IAI.reset(new InterleavedAccessInfo(*PSE, L, &*DT, &*LI, &*LAI));
|
|
|
+ IAI->analyzeInterleaving(false);
|
|
|
+ return {Plan, *IAI};
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+TEST_F(VPlanSlpTest, testSlpSimple_2) {
|
|
|
+ const char *ModuleString =
|
|
|
+ "%struct.Test = type { i32, i32 }\n"
|
|
|
+ "%struct.Test3 = type { i32, i32, i32 }\n"
|
|
|
+ "%struct.Test4xi8 = type { i8, i8, i8 }\n"
|
|
|
+ "define void @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* "
|
|
|
+ "nocapture readonly %B, %struct.Test* nocapture %C) {\n"
|
|
|
+ "entry:\n"
|
|
|
+ " br label %for.body\n"
|
|
|
+ "for.body: ; preds = %for.body, "
|
|
|
+ "%entry\n"
|
|
|
+ " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
|
|
|
+ " %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vA0 = load i32, i32* %A0, align 4\n"
|
|
|
+ " %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vB0 = load i32, i32* %B0, align 4\n"
|
|
|
+ " %add0 = add nsw i32 %vA0, %vB0\n"
|
|
|
+ " %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vA1 = load i32, i32* %A1, align 4\n"
|
|
|
+ " %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vB1 = load i32, i32* %B1, align 4\n"
|
|
|
+ " %add1 = add nsw i32 %vA1, %vB1\n"
|
|
|
+ " %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " store i32 %add0, i32* %C0, align 4\n"
|
|
|
+ " %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " store i32 %add1, i32* %C1, align 4\n"
|
|
|
+ " %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"
|
|
|
+ " %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"
|
|
|
+ " br i1 %exitcond, label %for.cond.cleanup, label %for.body\n"
|
|
|
+ "for.cond.cleanup: ; preds = %for.body\n"
|
|
|
+ " ret void\n"
|
|
|
+ "}\n";
|
|
|
+
|
|
|
+ Module &M = parseModule(ModuleString);
|
|
|
+
|
|
|
+ Function *F = M.getFunction("add_x2");
|
|
|
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
|
|
|
+ auto Plan = buildHCFG(LoopHeader);
|
|
|
+ auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);
|
|
|
+
|
|
|
+ VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();
|
|
|
+ EXPECT_NE(nullptr, Entry->getSingleSuccessor());
|
|
|
+ VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();
|
|
|
+
|
|
|
+ VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 12));
|
|
|
+ VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 14));
|
|
|
+
|
|
|
+ VPlanSlp Slp(VPIAI, *Body);
|
|
|
+ SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};
|
|
|
+ VPInstruction *CombinedStore = Slp.buildGraph(StoreRoot);
|
|
|
+ EXPECT_EQ(64u, Slp.getWidestBundleBits());
|
|
|
+ EXPECT_EQ(VPInstruction::SLPStore, CombinedStore->getOpcode());
|
|
|
+
|
|
|
+ auto *CombinedAdd = cast<VPInstruction>(CombinedStore->getOperand(0));
|
|
|
+ EXPECT_EQ(Instruction::Add, CombinedAdd->getOpcode());
|
|
|
+
|
|
|
+ auto *CombinedLoadA = cast<VPInstruction>(CombinedAdd->getOperand(0));
|
|
|
+ auto *CombinedLoadB = cast<VPInstruction>(CombinedAdd->getOperand(1));
|
|
|
+ EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadA->getOpcode());
|
|
|
+ EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadB->getOpcode());
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(VPlanSlpTest, testSlpSimple_3) {
|
|
|
+ const char *ModuleString =
|
|
|
+ "%struct.Test = type { i32, i32 }\n"
|
|
|
+ "%struct.Test3 = type { i32, i32, i32 }\n"
|
|
|
+ "%struct.Test4xi8 = type { i8, i8, i8 }\n"
|
|
|
+ "define void @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* "
|
|
|
+ "nocapture readonly %B, %struct.Test* nocapture %C) {\n"
|
|
|
+ "entry:\n"
|
|
|
+ " br label %for.body\n"
|
|
|
+ "for.body: ; preds = %for.body, "
|
|
|
+ "%entry\n"
|
|
|
+ " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
|
|
|
+ " %A0 = getelementptr %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ " %indvars.iv, i32 0\n"
|
|
|
+ " %vA0 = load i32, i32* %A0, align 4\n"
|
|
|
+ " %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ " %indvars.iv, i32 0\n"
|
|
|
+ " %vB0 = load i32, i32* %B0, align 4\n"
|
|
|
+ " %add0 = add nsw i32 %vA0, %vB0\n"
|
|
|
+ " %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ " %indvars.iv, i32 1\n"
|
|
|
+ " %vA1 = load i32, i32* %A1, align 4\n"
|
|
|
+ " %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ " %indvars.iv, i32 1\n"
|
|
|
+ " %vB1 = load i32, i32* %B1, align 4\n"
|
|
|
+ " %add1 = add nsw i32 %vA1, %vB1\n"
|
|
|
+ " %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ " %indvars.iv, i32 0\n"
|
|
|
+ " store i32 %add0, i32* %C0, align 4\n"
|
|
|
+ " %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ " %indvars.iv, i32 1\n"
|
|
|
+ " store i32 %add1, i32* %C1, align 4\n"
|
|
|
+ " %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"
|
|
|
+ " %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"
|
|
|
+ " br i1 %exitcond, label %for.cond.cleanup, label %for.body\n"
|
|
|
+ "for.cond.cleanup: ; preds = %for.body\n"
|
|
|
+ " ret void\n"
|
|
|
+ "}\n";
|
|
|
+
|
|
|
+ Module &M = parseModule(ModuleString);
|
|
|
+
|
|
|
+ Function *F = M.getFunction("add_x2");
|
|
|
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
|
|
|
+ auto Plan = buildHCFG(LoopHeader);
|
|
|
+
|
|
|
+ VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();
|
|
|
+ EXPECT_NE(nullptr, Entry->getSingleSuccessor());
|
|
|
+ VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();
|
|
|
+
|
|
|
+ VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 12));
|
|
|
+ VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 14));
|
|
|
+
|
|
|
+ auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);
|
|
|
+
|
|
|
+ VPlanSlp Slp(VPIAI, *Body);
|
|
|
+ SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};
|
|
|
+ VPInstruction *CombinedStore = Slp.buildGraph(StoreRoot);
|
|
|
+ EXPECT_EQ(64u, Slp.getWidestBundleBits());
|
|
|
+ EXPECT_EQ(VPInstruction::SLPStore, CombinedStore->getOpcode());
|
|
|
+
|
|
|
+ auto *CombinedAdd = cast<VPInstruction>(CombinedStore->getOperand(0));
|
|
|
+ EXPECT_EQ(Instruction::Add, CombinedAdd->getOpcode());
|
|
|
+
|
|
|
+ auto *CombinedLoadA = cast<VPInstruction>(CombinedAdd->getOperand(0));
|
|
|
+ auto *CombinedLoadB = cast<VPInstruction>(CombinedAdd->getOperand(1));
|
|
|
+ EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadA->getOpcode());
|
|
|
+ EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadB->getOpcode());
|
|
|
+
|
|
|
+ VPInstruction *GetA = cast<VPInstruction>(&*std::next(Body->begin(), 1));
|
|
|
+ VPInstruction *GetB = cast<VPInstruction>(&*std::next(Body->begin(), 3));
|
|
|
+ EXPECT_EQ(GetA, CombinedLoadA->getOperand(0));
|
|
|
+ EXPECT_EQ(GetB, CombinedLoadB->getOperand(0));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(VPlanSlpTest, testSlpReuse_1) {
|
|
|
+ const char *ModuleString =
|
|
|
+ "%struct.Test = type { i32, i32 }\n"
|
|
|
+ "define void @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* "
|
|
|
+ "nocapture readonly %B, %struct.Test* nocapture %C) {\n"
|
|
|
+ "entry:\n"
|
|
|
+ " br label %for.body\n"
|
|
|
+ "for.body: ; preds = %for.body, "
|
|
|
+ "%entry\n"
|
|
|
+ " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
|
|
|
+ " %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vA0 = load i32, i32* %A0, align 4\n"
|
|
|
+ " %add0 = add nsw i32 %vA0, %vA0\n"
|
|
|
+ " %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vA1 = load i32, i32* %A1, align 4\n"
|
|
|
+ " %add1 = add nsw i32 %vA1, %vA1\n"
|
|
|
+ " %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " store i32 %add0, i32* %C0, align 4\n"
|
|
|
+ " %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " store i32 %add1, i32* %C1, align 4\n"
|
|
|
+ " %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"
|
|
|
+ " %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"
|
|
|
+ " br i1 %exitcond, label %for.cond.cleanup, label %for.body\n"
|
|
|
+ "for.cond.cleanup: ; preds = %for.body\n"
|
|
|
+ " ret void\n"
|
|
|
+ "}\n";
|
|
|
+
|
|
|
+ Module &M = parseModule(ModuleString);
|
|
|
+
|
|
|
+ Function *F = M.getFunction("add_x2");
|
|
|
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
|
|
|
+ auto Plan = buildHCFG(LoopHeader);
|
|
|
+ auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);
|
|
|
+
|
|
|
+ VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();
|
|
|
+ EXPECT_NE(nullptr, Entry->getSingleSuccessor());
|
|
|
+ VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();
|
|
|
+
|
|
|
+ VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 8));
|
|
|
+ VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 10));
|
|
|
+
|
|
|
+ VPlanSlp Slp(VPIAI, *Body);
|
|
|
+ SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};
|
|
|
+ VPInstruction *CombinedStore = Slp.buildGraph(StoreRoot);
|
|
|
+ EXPECT_EQ(64u, Slp.getWidestBundleBits());
|
|
|
+ EXPECT_EQ(VPInstruction::SLPStore, CombinedStore->getOpcode());
|
|
|
+
|
|
|
+ auto *CombinedAdd = cast<VPInstruction>(CombinedStore->getOperand(0));
|
|
|
+ EXPECT_EQ(Instruction::Add, CombinedAdd->getOpcode());
|
|
|
+
|
|
|
+ auto *CombinedLoadA = cast<VPInstruction>(CombinedAdd->getOperand(0));
|
|
|
+ EXPECT_EQ(CombinedLoadA, CombinedAdd->getOperand(1));
|
|
|
+ EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadA->getOpcode());
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(VPlanSlpTest, testSlpReuse_2) {
|
|
|
+ const char *ModuleString =
|
|
|
+ "%struct.Test = type { i32, i32 }\n"
|
|
|
+ "define i32 @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* "
|
|
|
+ "nocapture readonly %B, %struct.Test* nocapture %C) {\n"
|
|
|
+ "entry:\n"
|
|
|
+ " br label %for.body\n"
|
|
|
+ "for.body: ; preds = %for.body, "
|
|
|
+ "%entry\n"
|
|
|
+ " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
|
|
|
+ " %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vA0 = load i32, i32* %A0, align 4\n"
|
|
|
+ " %add0 = add nsw i32 %vA0, %vA0\n"
|
|
|
+ " %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " store i32 %add0, i32* %C0, align 4\n"
|
|
|
+ " %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vA1 = load i32, i32* %A1, align 4\n"
|
|
|
+ " %add1 = add nsw i32 %vA1, %vA1\n"
|
|
|
+ " %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " store i32 %add1, i32* %C1, align 4\n"
|
|
|
+ " %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"
|
|
|
+ " %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"
|
|
|
+ " br i1 %exitcond, label %for.cond.cleanup, label %for.body\n"
|
|
|
+ "for.cond.cleanup: ; preds = %for.body\n"
|
|
|
+ " ret i32 %vA1\n"
|
|
|
+ "}\n";
|
|
|
+
|
|
|
+ Module &M = parseModule(ModuleString);
|
|
|
+
|
|
|
+ Function *F = M.getFunction("add_x2");
|
|
|
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
|
|
|
+ auto Plan = buildHCFG(LoopHeader);
|
|
|
+ auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);
|
|
|
+
|
|
|
+ VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();
|
|
|
+ EXPECT_NE(nullptr, Entry->getSingleSuccessor());
|
|
|
+ VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();
|
|
|
+
|
|
|
+ VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 5));
|
|
|
+ VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 10));
|
|
|
+
|
|
|
+ VPlanSlp Slp(VPIAI, *Body);
|
|
|
+ SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};
|
|
|
+ Slp.buildGraph(StoreRoot);
|
|
|
+ EXPECT_FALSE(Slp.isCompletelySLP());
|
|
|
+}
|
|
|
+
|
|
|
+static void checkReorderExample(VPInstruction *Store1, VPInstruction *Store2,
|
|
|
+ VPBasicBlock *Body,
|
|
|
+ VPInterleavedAccessInfo &&IAI) {
|
|
|
+ VPlanSlp Slp(IAI, *Body);
|
|
|
+ SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};
|
|
|
+ VPInstruction *CombinedStore = Slp.buildGraph(StoreRoot);
|
|
|
+
|
|
|
+ EXPECT_TRUE(Slp.isCompletelySLP());
|
|
|
+ EXPECT_EQ(CombinedStore->getOpcode(), VPInstruction::SLPStore);
|
|
|
+
|
|
|
+ VPInstruction *CombinedAdd =
|
|
|
+ cast<VPInstruction>(CombinedStore->getOperand(0));
|
|
|
+ EXPECT_EQ(CombinedAdd->getOpcode(), Instruction::Add);
|
|
|
+
|
|
|
+ VPInstruction *CombinedMulAB =
|
|
|
+ cast<VPInstruction>(CombinedAdd->getOperand(0));
|
|
|
+ VPInstruction *CombinedMulCD =
|
|
|
+ cast<VPInstruction>(CombinedAdd->getOperand(1));
|
|
|
+ EXPECT_EQ(CombinedMulAB->getOpcode(), Instruction::Mul);
|
|
|
+
|
|
|
+ VPInstruction *CombinedLoadA =
|
|
|
+ cast<VPInstruction>(CombinedMulAB->getOperand(0));
|
|
|
+ EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadA->getOpcode());
|
|
|
+ VPInstruction *LoadvA0 = cast<VPInstruction>(&*std::next(Body->begin(), 2));
|
|
|
+ VPInstruction *LoadvA1 = cast<VPInstruction>(&*std::next(Body->begin(), 12));
|
|
|
+ EXPECT_EQ(LoadvA0->getOperand(0), CombinedLoadA->getOperand(0));
|
|
|
+ EXPECT_EQ(LoadvA1->getOperand(0), CombinedLoadA->getOperand(1));
|
|
|
+
|
|
|
+ VPInstruction *CombinedLoadB =
|
|
|
+ cast<VPInstruction>(CombinedMulAB->getOperand(1));
|
|
|
+ EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadB->getOpcode());
|
|
|
+ VPInstruction *LoadvB0 = cast<VPInstruction>(&*std::next(Body->begin(), 4));
|
|
|
+ VPInstruction *LoadvB1 = cast<VPInstruction>(&*std::next(Body->begin(), 14));
|
|
|
+ EXPECT_EQ(LoadvB0->getOperand(0), CombinedLoadB->getOperand(0));
|
|
|
+ EXPECT_EQ(LoadvB1->getOperand(0), CombinedLoadB->getOperand(1));
|
|
|
+
|
|
|
+ EXPECT_EQ(CombinedMulCD->getOpcode(), Instruction::Mul);
|
|
|
+
|
|
|
+ VPInstruction *CombinedLoadC =
|
|
|
+ cast<VPInstruction>(CombinedMulCD->getOperand(0));
|
|
|
+ EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadC->getOpcode());
|
|
|
+ VPInstruction *LoadvC0 = cast<VPInstruction>(&*std::next(Body->begin(), 7));
|
|
|
+ VPInstruction *LoadvC1 = cast<VPInstruction>(&*std::next(Body->begin(), 17));
|
|
|
+ EXPECT_EQ(LoadvC0->getOperand(0), CombinedLoadC->getOperand(0));
|
|
|
+ EXPECT_EQ(LoadvC1->getOperand(0), CombinedLoadC->getOperand(1));
|
|
|
+
|
|
|
+ VPInstruction *CombinedLoadD =
|
|
|
+ cast<VPInstruction>(CombinedMulCD->getOperand(1));
|
|
|
+ EXPECT_EQ(VPInstruction::SLPLoad, CombinedLoadD->getOpcode());
|
|
|
+ VPInstruction *LoadvD0 = cast<VPInstruction>(&*std::next(Body->begin(), 9));
|
|
|
+ VPInstruction *LoadvD1 = cast<VPInstruction>(&*std::next(Body->begin(), 19));
|
|
|
+ EXPECT_EQ(LoadvD0->getOperand(0), CombinedLoadD->getOperand(0));
|
|
|
+ EXPECT_EQ(LoadvD1->getOperand(0), CombinedLoadD->getOperand(1));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(VPlanSlpTest, testSlpReorder_1) {
|
|
|
+ LLVMContext Ctx;
|
|
|
+ const char *ModuleString =
|
|
|
+ "%struct.Test = type { i32, i32 }\n"
|
|
|
+ "define void @add_x3(%struct.Test* %A, %struct.Test* %B, %struct.Test* "
|
|
|
+ "%C, %struct.Test* %D, %struct.Test* %E) {\n"
|
|
|
+ "entry:\n"
|
|
|
+ " br label %for.body\n"
|
|
|
+ "for.body: ; preds = %for.body, "
|
|
|
+ "%entry\n"
|
|
|
+ " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
|
|
|
+ " %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vA0 = load i32, i32* %A0, align 4\n"
|
|
|
+ " %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vB0 = load i32, i32* %B0, align 4\n"
|
|
|
+ " %mul11 = mul nsw i32 %vA0, %vB0\n"
|
|
|
+ " %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vC0 = load i32, i32* %C0, align 4\n"
|
|
|
+ " %D0 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vD0 = load i32, i32* %D0, align 4\n"
|
|
|
+ " %mul12 = mul nsw i32 %vC0, %vD0\n"
|
|
|
+ " %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vA1 = load i32, i32* %A1, align 4\n"
|
|
|
+ " %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vB1 = load i32, i32* %B1, align 4\n"
|
|
|
+ " %mul21 = mul nsw i32 %vA1, %vB1\n"
|
|
|
+ " %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vC1 = load i32, i32* %C1, align 4\n"
|
|
|
+ " %D1 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vD1 = load i32, i32* %D1, align 4\n"
|
|
|
+ " %mul22 = mul nsw i32 %vC1, %vD1\n"
|
|
|
+ " %add1 = add nsw i32 %mul11, %mul12\n"
|
|
|
+ " %add2 = add nsw i32 %mul22, %mul21\n"
|
|
|
+ " %E0 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " store i32 %add1, i32* %E0, align 4\n"
|
|
|
+ " %E1 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " store i32 %add2, i32* %E1, align 4\n"
|
|
|
+ " %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"
|
|
|
+ " %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"
|
|
|
+ " br i1 %exitcond, label %for.cond.cleanup, label %for.body\n"
|
|
|
+ "for.cond.cleanup: ; preds = %for.body\n"
|
|
|
+ " ret void\n"
|
|
|
+ "}\n";
|
|
|
+
|
|
|
+ Module &M = parseModule(ModuleString);
|
|
|
+
|
|
|
+ Function *F = M.getFunction("add_x3");
|
|
|
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
|
|
|
+ auto Plan = buildHCFG(LoopHeader);
|
|
|
+
|
|
|
+ VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();
|
|
|
+ EXPECT_NE(nullptr, Entry->getSingleSuccessor());
|
|
|
+ VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();
|
|
|
+
|
|
|
+ VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 24));
|
|
|
+ VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 26));
|
|
|
+
|
|
|
+ checkReorderExample(
|
|
|
+ Store1, Store2, Body,
|
|
|
+ getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(VPlanSlpTest, testSlpReorder_2) {
|
|
|
+ LLVMContext Ctx;
|
|
|
+ const char *ModuleString =
|
|
|
+ "%struct.Test = type { i32, i32 }\n"
|
|
|
+ "define void @add_x3(%struct.Test* %A, %struct.Test* %B, %struct.Test* "
|
|
|
+ "%C, %struct.Test* %D, %struct.Test* %E) {\n"
|
|
|
+ "entry:\n"
|
|
|
+ " br label %for.body\n"
|
|
|
+ "for.body: ; preds = %for.body, "
|
|
|
+ "%entry\n"
|
|
|
+ " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
|
|
|
+ " %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vA0 = load i32, i32* %A0, align 4\n"
|
|
|
+ " %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vB0 = load i32, i32* %B0, align 4\n"
|
|
|
+ " %mul11 = mul nsw i32 %vA0, %vB0\n"
|
|
|
+ " %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vC0 = load i32, i32* %C0, align 4\n"
|
|
|
+ " %D0 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vD0 = load i32, i32* %D0, align 4\n"
|
|
|
+ " %mul12 = mul nsw i32 %vC0, %vD0\n"
|
|
|
+ " %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vA1 = load i32, i32* %A1, align 4\n"
|
|
|
+ " %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vB1 = load i32, i32* %B1, align 4\n"
|
|
|
+ " %mul21 = mul nsw i32 %vB1, %vA1\n"
|
|
|
+ " %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vC1 = load i32, i32* %C1, align 4\n"
|
|
|
+ " %D1 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vD1 = load i32, i32* %D1, align 4\n"
|
|
|
+ " %mul22 = mul nsw i32 %vD1, %vC1\n"
|
|
|
+ " %add1 = add nsw i32 %mul11, %mul12\n"
|
|
|
+ " %add2 = add nsw i32 %mul22, %mul21\n"
|
|
|
+ " %E0 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " store i32 %add1, i32* %E0, align 4\n"
|
|
|
+ " %E1 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " store i32 %add2, i32* %E1, align 4\n"
|
|
|
+ " %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"
|
|
|
+ " %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"
|
|
|
+ " br i1 %exitcond, label %for.cond.cleanup, label %for.body\n"
|
|
|
+ "for.cond.cleanup: ; preds = %for.body\n"
|
|
|
+ " ret void\n"
|
|
|
+ "}\n";
|
|
|
+
|
|
|
+ Module &M = parseModule(ModuleString);
|
|
|
+
|
|
|
+ Function *F = M.getFunction("add_x3");
|
|
|
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
|
|
|
+ auto Plan = buildHCFG(LoopHeader);
|
|
|
+
|
|
|
+ VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();
|
|
|
+ EXPECT_NE(nullptr, Entry->getSingleSuccessor());
|
|
|
+ VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();
|
|
|
+
|
|
|
+ VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 24));
|
|
|
+ VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 26));
|
|
|
+
|
|
|
+ checkReorderExample(
|
|
|
+ Store1, Store2, Body,
|
|
|
+ getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(VPlanSlpTest, testSlpReorder_3) {
|
|
|
+ LLVMContext Ctx;
|
|
|
+ const char *ModuleString =
|
|
|
+ "%struct.Test = type { i32, i32 }\n"
|
|
|
+ "define void @add_x3(%struct.Test* %A, %struct.Test* %B, %struct.Test* "
|
|
|
+ "%C, %struct.Test* %D, %struct.Test* %E) {\n"
|
|
|
+ "entry:\n"
|
|
|
+ " br label %for.body\n"
|
|
|
+ "for.body: ; preds = %for.body, "
|
|
|
+ "%entry\n"
|
|
|
+ " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
|
|
|
+ " %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vA1 = load i32, i32* %A1, align 4\n"
|
|
|
+ " %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vB0 = load i32, i32* %B0, align 4\n"
|
|
|
+ " %mul11 = mul nsw i32 %vA1, %vB0\n"
|
|
|
+ " %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vC0 = load i32, i32* %C0, align 4\n"
|
|
|
+ " %D0 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vD0 = load i32, i32* %D0, align 4\n"
|
|
|
+ " %mul12 = mul nsw i32 %vC0, %vD0\n"
|
|
|
+ " %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vA0 = load i32, i32* %A0, align 4\n"
|
|
|
+ " %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vB1 = load i32, i32* %B1, align 4\n"
|
|
|
+ " %mul21 = mul nsw i32 %vB1, %vA0\n"
|
|
|
+ " %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vC1 = load i32, i32* %C1, align 4\n"
|
|
|
+ " %D1 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vD1 = load i32, i32* %D1, align 4\n"
|
|
|
+ " %mul22 = mul nsw i32 %vD1, %vC1\n"
|
|
|
+ " %add1 = add nsw i32 %mul11, %mul12\n"
|
|
|
+ " %add2 = add nsw i32 %mul22, %mul21\n"
|
|
|
+ " %E0 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " store i32 %add1, i32* %E0, align 4\n"
|
|
|
+ " %E1 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " store i32 %add2, i32* %E1, align 4\n"
|
|
|
+ " %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"
|
|
|
+ " %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"
|
|
|
+ " br i1 %exitcond, label %for.cond.cleanup, label %for.body\n"
|
|
|
+ "for.cond.cleanup: ; preds = %for.body\n"
|
|
|
+ " ret void\n"
|
|
|
+ "}\n";
|
|
|
+
|
|
|
+ Module &M = parseModule(ModuleString);
|
|
|
+
|
|
|
+ Function *F = M.getFunction("add_x3");
|
|
|
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
|
|
|
+ auto Plan = buildHCFG(LoopHeader);
|
|
|
+
|
|
|
+ VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();
|
|
|
+ EXPECT_NE(nullptr, Entry->getSingleSuccessor());
|
|
|
+ VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();
|
|
|
+
|
|
|
+ VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 24));
|
|
|
+ VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 26));
|
|
|
+
|
|
|
+ auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);
|
|
|
+ VPlanSlp Slp(VPIAI, *Body);
|
|
|
+ SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};
|
|
|
+ EXPECT_EQ(nullptr, Slp.buildGraph(StoreRoot));
|
|
|
+
|
|
|
+ // FIXME Need to select better first value for lane0.
|
|
|
+ EXPECT_FALSE(Slp.isCompletelySLP());
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(VPlanSlpTest, testSlpReorder_4) {
|
|
|
+ LLVMContext Ctx;
|
|
|
+ const char *ModuleString =
|
|
|
+ "%struct.Test = type { i32, i32 }\n"
|
|
|
+ "define void @add_x3(%struct.Test* %A, %struct.Test* %B, %struct.Test* "
|
|
|
+ "%C, %struct.Test* %D, %struct.Test* %E) {\n"
|
|
|
+ "entry:\n"
|
|
|
+ " br label %for.body\n"
|
|
|
+ "for.body: ; preds = %for.body, "
|
|
|
+ "%entry\n"
|
|
|
+ " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
|
|
|
+ " %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vA0 = load i32, i32* %A0, align 4\n"
|
|
|
+ " %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vB0 = load i32, i32* %B0, align 4\n"
|
|
|
+ " %mul11 = mul nsw i32 %vA0, %vB0\n"
|
|
|
+ " %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vC0 = load i32, i32* %C0, align 4\n"
|
|
|
+ " %D0 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vD0 = load i32, i32* %D0, align 4\n"
|
|
|
+ " %mul12 = mul nsw i32 %vC0, %vD0\n"
|
|
|
+ " %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vA1 = load i32, i32* %A1, align 4\n"
|
|
|
+ " %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vB1 = load i32, i32* %B1, align 4\n"
|
|
|
+ " %mul21 = mul nsw i32 %vA1, %vB1\n"
|
|
|
+ " %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vC1 = load i32, i32* %C1, align 4\n"
|
|
|
+ " %D1 = getelementptr inbounds %struct.Test, %struct.Test* %D, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vD1 = load i32, i32* %D1, align 4\n"
|
|
|
+ " %mul22 = mul nsw i32 %vC1, %vD1\n"
|
|
|
+ " %add1 = add nsw i32 %mul11, %mul12\n"
|
|
|
+ " %add2 = add nsw i32 %mul22, %mul21\n"
|
|
|
+ " %E0 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " store i32 %add1, i32* %E0, align 4\n"
|
|
|
+ " %E1 = getelementptr inbounds %struct.Test, %struct.Test* %E, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " store i32 %add2, i32* %E1, align 4\n"
|
|
|
+ " %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"
|
|
|
+ " %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"
|
|
|
+ " br i1 %exitcond, label %for.cond.cleanup, label %for.body\n"
|
|
|
+ "for.cond.cleanup: ; preds = %for.body\n"
|
|
|
+ " ret void\n"
|
|
|
+ "}\n";
|
|
|
+
|
|
|
+ Module &M = parseModule(ModuleString);
|
|
|
+
|
|
|
+ Function *F = M.getFunction("add_x3");
|
|
|
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
|
|
|
+ auto Plan = buildHCFG(LoopHeader);
|
|
|
+
|
|
|
+ VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();
|
|
|
+ EXPECT_NE(nullptr, Entry->getSingleSuccessor());
|
|
|
+ VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();
|
|
|
+
|
|
|
+ VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 24));
|
|
|
+ VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 26));
|
|
|
+
|
|
|
+ checkReorderExample(
|
|
|
+ Store1, Store2, Body,
|
|
|
+ getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan));
|
|
|
+}
|
|
|
+
|
|
|
+// Make sure we do not combine instructions with operands in different BBs.
|
|
|
+TEST_F(VPlanSlpTest, testInstrsInDifferentBBs) {
|
|
|
+ const char *ModuleString =
|
|
|
+ "%struct.Test = type { i32, i32 }\n"
|
|
|
+ "%struct.Test3 = type { i32, i32, i32 }\n"
|
|
|
+ "%struct.Test4xi8 = type { i8, i8, i8 }\n"
|
|
|
+ "define void @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* "
|
|
|
+ "nocapture readonly %B, %struct.Test* nocapture %C) {\n"
|
|
|
+ "entry:\n"
|
|
|
+ " br label %for.body\n"
|
|
|
+ "for.body: ; preds = %for.body, "
|
|
|
+ "%entry\n"
|
|
|
+ " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
|
|
|
+ " %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vA0 = load i32, i32* %A0, align 4\n"
|
|
|
+ " %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vB0 = load i32, i32* %B0, align 4\n"
|
|
|
+ " %add0 = add nsw i32 %vA0, %vB0\n"
|
|
|
+ " %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vA1 = load i32, i32* %A1, align 4\n"
|
|
|
+ " %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " br label %bb2\n"
|
|
|
+ "bb2:\n"
|
|
|
+ " %vB1 = load i32, i32* %B1, align 4\n"
|
|
|
+ " %add1 = add nsw i32 %vA1, %vB1\n"
|
|
|
+ " %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " store i32 %add0, i32* %C0, align 4\n"
|
|
|
+ " %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " store i32 %add1, i32* %C1, align 4\n"
|
|
|
+ " %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"
|
|
|
+ " %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"
|
|
|
+ " br i1 %exitcond, label %for.cond.cleanup, label %for.body\n"
|
|
|
+ "for.cond.cleanup: ; preds = %for.body\n"
|
|
|
+ " ret void\n"
|
|
|
+ "}\n";
|
|
|
+
|
|
|
+ Module &M = parseModule(ModuleString);
|
|
|
+
|
|
|
+ Function *F = M.getFunction("add_x2");
|
|
|
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
|
|
|
+ auto Plan = buildHCFG(LoopHeader);
|
|
|
+ auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);
|
|
|
+
|
|
|
+ VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();
|
|
|
+ EXPECT_NE(nullptr, Entry->getSingleSuccessor());
|
|
|
+ VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();
|
|
|
+ VPBasicBlock *BB2 = Body->getSingleSuccessor()->getEntryBasicBlock();
|
|
|
+
|
|
|
+ VPInstruction *Store1 = cast<VPInstruction>(&*std::next(BB2->begin(), 3));
|
|
|
+ VPInstruction *Store2 = cast<VPInstruction>(&*std::next(BB2->begin(), 5));
|
|
|
+
|
|
|
+ VPlanSlp Slp(VPIAI, *BB2);
|
|
|
+ SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};
|
|
|
+ EXPECT_EQ(nullptr, Slp.buildGraph(StoreRoot));
|
|
|
+ EXPECT_EQ(0u, Slp.getWidestBundleBits());
|
|
|
+}
|
|
|
+
|
|
|
+// Make sure we do not combine instructions with operands in different BBs.
|
|
|
+TEST_F(VPlanSlpTest, testInstrsInDifferentBBs2) {
|
|
|
+ const char *ModuleString =
|
|
|
+ "%struct.Test = type { i32, i32 }\n"
|
|
|
+ "%struct.Test3 = type { i32, i32, i32 }\n"
|
|
|
+ "%struct.Test4xi8 = type { i8, i8, i8 }\n"
|
|
|
+ "define void @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* "
|
|
|
+ "nocapture readonly %B, %struct.Test* nocapture %C) {\n"
|
|
|
+ "entry:\n"
|
|
|
+ " br label %for.body\n"
|
|
|
+ "for.body: ; preds = %for.body, "
|
|
|
+ "%entry\n"
|
|
|
+ " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
|
|
|
+ " %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vA0 = load i32, i32* %A0, align 4\n"
|
|
|
+ " %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vB0 = load i32, i32* %B0, align 4\n"
|
|
|
+ " %add0 = add nsw i32 %vA0, %vB0\n"
|
|
|
+ " %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vA1 = load i32, i32* %A1, align 4\n"
|
|
|
+ " %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vB1 = load i32, i32* %B1, align 4\n"
|
|
|
+ " %add1 = add nsw i32 %vA1, %vB1\n"
|
|
|
+ " br label %bb2\n"
|
|
|
+ "bb2:\n"
|
|
|
+ " %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " store i32 %add0, i32* %C0, align 4\n"
|
|
|
+ " %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " store i32 %add1, i32* %C1, align 4\n"
|
|
|
+ " %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"
|
|
|
+ " %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"
|
|
|
+ " br i1 %exitcond, label %for.cond.cleanup, label %for.body\n"
|
|
|
+ "for.cond.cleanup: ; preds = %for.body\n"
|
|
|
+ " ret void\n"
|
|
|
+ "}\n";
|
|
|
+
|
|
|
+ Module &M = parseModule(ModuleString);
|
|
|
+
|
|
|
+ Function *F = M.getFunction("add_x2");
|
|
|
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
|
|
|
+ auto Plan = buildHCFG(LoopHeader);
|
|
|
+ auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);
|
|
|
+
|
|
|
+ VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();
|
|
|
+ EXPECT_NE(nullptr, Entry->getSingleSuccessor());
|
|
|
+ VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();
|
|
|
+ VPBasicBlock *BB2 = Body->getSingleSuccessor()->getEntryBasicBlock();
|
|
|
+
|
|
|
+ VPInstruction *Store1 = cast<VPInstruction>(&*std::next(BB2->begin(), 1));
|
|
|
+ VPInstruction *Store2 = cast<VPInstruction>(&*std::next(BB2->begin(), 3));
|
|
|
+
|
|
|
+ VPlanSlp Slp(VPIAI, *BB2);
|
|
|
+ SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};
|
|
|
+ EXPECT_EQ(nullptr, Slp.buildGraph(StoreRoot));
|
|
|
+ EXPECT_EQ(0u, Slp.getWidestBundleBits());
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(VPlanSlpTest, testSlpAtomicLoad) {
|
|
|
+ const char *ModuleString =
|
|
|
+ "%struct.Test = type { i32, i32 }\n"
|
|
|
+ "%struct.Test3 = type { i32, i32, i32 }\n"
|
|
|
+ "%struct.Test4xi8 = type { i8, i8, i8 }\n"
|
|
|
+ "define void @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* "
|
|
|
+ "nocapture readonly %B, %struct.Test* nocapture %C) {\n"
|
|
|
+ "entry:\n"
|
|
|
+ " br label %for.body\n"
|
|
|
+ "for.body: ; preds = %for.body, "
|
|
|
+ "%entry\n"
|
|
|
+ " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
|
|
|
+ " %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vA0 = load atomic i32, i32* %A0 monotonic, align 4\n"
|
|
|
+ " %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vB0 = load i32, i32* %B0, align 4\n"
|
|
|
+ " %add0 = add nsw i32 %vA0, %vB0\n"
|
|
|
+ " %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vA1 = load i32, i32* %A1, align 4\n"
|
|
|
+ " %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vB1 = load i32, i32* %B1, align 4\n"
|
|
|
+ " %add1 = add nsw i32 %vA1, %vB1\n"
|
|
|
+ " %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " store i32 %add0, i32* %C0, align 4\n"
|
|
|
+ " %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " store i32 %add1, i32* %C1, align 4\n"
|
|
|
+ " %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"
|
|
|
+ " %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"
|
|
|
+ " br i1 %exitcond, label %for.cond.cleanup, label %for.body\n"
|
|
|
+ "for.cond.cleanup: ; preds = %for.body\n"
|
|
|
+ " ret void\n"
|
|
|
+ "}\n";
|
|
|
+
|
|
|
+ Module &M = parseModule(ModuleString);
|
|
|
+
|
|
|
+ Function *F = M.getFunction("add_x2");
|
|
|
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
|
|
|
+ auto Plan = buildHCFG(LoopHeader);
|
|
|
+ auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);
|
|
|
+
|
|
|
+ VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();
|
|
|
+ EXPECT_NE(nullptr, Entry->getSingleSuccessor());
|
|
|
+ VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();
|
|
|
+
|
|
|
+ VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 12));
|
|
|
+ VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 14));
|
|
|
+
|
|
|
+ VPlanSlp Slp(VPIAI, *Body);
|
|
|
+ SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};
|
|
|
+ EXPECT_EQ(nullptr, Slp.buildGraph(StoreRoot));
|
|
|
+ EXPECT_FALSE(Slp.isCompletelySLP());
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(VPlanSlpTest, testSlpAtomicStore) {
|
|
|
+ const char *ModuleString =
|
|
|
+ "%struct.Test = type { i32, i32 }\n"
|
|
|
+ "%struct.Test3 = type { i32, i32, i32 }\n"
|
|
|
+ "%struct.Test4xi8 = type { i8, i8, i8 }\n"
|
|
|
+ "define void @add_x2(%struct.Test* nocapture readonly %A, %struct.Test* "
|
|
|
+ "nocapture readonly %B, %struct.Test* nocapture %C) {\n"
|
|
|
+ "entry:\n"
|
|
|
+ " br label %for.body\n"
|
|
|
+ "for.body: ; preds = %for.body, "
|
|
|
+ "%entry\n"
|
|
|
+ " %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]\n"
|
|
|
+ " %A0 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vA0 = load i32, i32* %A0, align 4\n"
|
|
|
+ " %B0 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " %vB0 = load i32, i32* %B0, align 4\n"
|
|
|
+ " %add0 = add nsw i32 %vA0, %vB0\n"
|
|
|
+ " %A1 = getelementptr inbounds %struct.Test, %struct.Test* %A, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vA1 = load i32, i32* %A1, align 4\n"
|
|
|
+ " %B1 = getelementptr inbounds %struct.Test, %struct.Test* %B, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " %vB1 = load i32, i32* %B1, align 4\n"
|
|
|
+ " %add1 = add nsw i32 %vA1, %vB1\n"
|
|
|
+ " %C0 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 0\n"
|
|
|
+ " store atomic i32 %add0, i32* %C0 monotonic, align 4\n"
|
|
|
+ " %C1 = getelementptr inbounds %struct.Test, %struct.Test* %C, i64 "
|
|
|
+ "%indvars.iv, i32 1\n"
|
|
|
+ " store i32 %add1, i32* %C1, align 4\n"
|
|
|
+ " %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"
|
|
|
+ " %exitcond = icmp eq i64 %indvars.iv.next, 1024\n"
|
|
|
+ " br i1 %exitcond, label %for.cond.cleanup, label %for.body\n"
|
|
|
+ "for.cond.cleanup: ; preds = %for.body\n"
|
|
|
+ " ret void\n"
|
|
|
+ "}\n";
|
|
|
+
|
|
|
+ Module &M = parseModule(ModuleString);
|
|
|
+
|
|
|
+ Function *F = M.getFunction("add_x2");
|
|
|
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();
|
|
|
+ auto Plan = buildHCFG(LoopHeader);
|
|
|
+ auto VPIAI = getInterleavedAccessInfo(*F, LI->getLoopFor(LoopHeader), *Plan);
|
|
|
+
|
|
|
+ VPBlockBase *Entry = Plan->getEntry()->getEntryBasicBlock();
|
|
|
+ EXPECT_NE(nullptr, Entry->getSingleSuccessor());
|
|
|
+ VPBasicBlock *Body = Entry->getSingleSuccessor()->getEntryBasicBlock();
|
|
|
+
|
|
|
+ VPInstruction *Store1 = cast<VPInstruction>(&*std::next(Body->begin(), 12));
|
|
|
+ VPInstruction *Store2 = cast<VPInstruction>(&*std::next(Body->begin(), 14));
|
|
|
+
|
|
|
+ VPlanSlp Slp(VPIAI, *Body);
|
|
|
+ SmallVector<VPValue *, 4> StoreRoot = {Store1, Store2};
|
|
|
+ Slp.buildGraph(StoreRoot);
|
|
|
+ EXPECT_FALSE(Slp.isCompletelySLP());
|
|
|
+}
|
|
|
+
|
|
|
+} // namespace
|
|
|
+} // namespace llvm
|