| //===- llvm/unittest/Transforms/Vectorize/VPlanSlpTest.cpp ---------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "../lib/Transforms/Vectorize/VPlan.h" |
| #include "../lib/Transforms/Vectorize/VPlanHCFGBuilder.h" |
| #include "VPlanTestBase.h" |
| #include "llvm/Analysis/TargetLibraryInfo.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()); |
| |
| delete CombinedStore; |
| delete CombinedAdd; |
| delete CombinedLoadA; |
| delete CombinedLoadB; |
| } |
| |
| 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)); |
| |
| delete CombinedStore; |
| delete CombinedAdd; |
| delete CombinedLoadA; |
| delete CombinedLoadB; |
| } |
| |
| 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()); |
| |
| delete CombinedStore; |
| delete CombinedAdd; |
| delete CombinedLoadA; |
| } |
| |
| 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)); |
| |
| delete CombinedStore; |
| delete CombinedAdd; |
| delete CombinedMulAB; |
| delete CombinedMulCD; |
| delete CombinedLoadA; |
| delete CombinedLoadB; |
| delete CombinedLoadC; |
| delete CombinedLoadD; |
| } |
| |
| 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 |