507 lines · cpp
1//===- LegalityTest.cpp ---------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/Transforms/Vectorize/SandboxVectorizer/Legality.h"10#include "llvm/Analysis/AssumptionCache.h"11#include "llvm/Analysis/BasicAliasAnalysis.h"12#include "llvm/Analysis/LoopInfo.h"13#include "llvm/Analysis/ScalarEvolution.h"14#include "llvm/Analysis/TargetLibraryInfo.h"15#include "llvm/AsmParser/Parser.h"16#include "llvm/IR/DataLayout.h"17#include "llvm/IR/Dominators.h"18#include "llvm/SandboxIR/Function.h"19#include "llvm/SandboxIR/Instruction.h"20#include "llvm/Support/SourceMgr.h"21#include "llvm/Transforms/Vectorize/SandboxVectorizer/InstrMaps.h"22#include "gmock/gmock.h"23#include "gtest/gtest.h"24 25using namespace llvm;26 27struct LegalityTest : public testing::Test {28 LLVMContext C;29 std::unique_ptr<Module> M;30 std::unique_ptr<DominatorTree> DT;31 std::unique_ptr<TargetLibraryInfoImpl> TLII;32 std::unique_ptr<TargetLibraryInfo> TLI;33 std::unique_ptr<AssumptionCache> AC;34 std::unique_ptr<LoopInfo> LI;35 std::unique_ptr<ScalarEvolution> SE;36 std::unique_ptr<BasicAAResult> BAA;37 std::unique_ptr<AAResults> AA;38 39 void getAnalyses(llvm::Function &LLVMF) {40 DT = std::make_unique<DominatorTree>(LLVMF);41 AC = std::make_unique<AssumptionCache>(LLVMF);42 LI = std::make_unique<LoopInfo>(*DT);43 SE = std::make_unique<ScalarEvolution>(LLVMF, *TLI, *AC, *DT, *LI);44 BAA = std::make_unique<BasicAAResult>(LLVMF.getParent()->getDataLayout(),45 LLVMF, *TLI, *AC, DT.get());46 AA = std::make_unique<AAResults>(*TLI);47 AA->addAAResult(*BAA);48 }49 50 void parseIR(LLVMContext &C, const char *IR) {51 SMDiagnostic Err;52 M = parseAssemblyString(IR, Err, C);53 if (!M) {54 Err.print("LegalityTest", errs());55 return;56 }57 58 TLII = std::make_unique<TargetLibraryInfoImpl>(M->getTargetTriple());59 TLI = std::make_unique<TargetLibraryInfo>(*TLII);60 }61};62 63static sandboxir::BasicBlock *getBasicBlockByName(sandboxir::Function *F,64 StringRef Name) {65 for (sandboxir::BasicBlock &BB : *F)66 if (BB.getName() == Name)67 return &BB;68 llvm_unreachable("Expected to find basic block!");69}70 71TEST_F(LegalityTest, LegalitySkipSchedule) {72 parseIR(C, R"IR(73define void @foo(ptr %ptr, <2 x float> %vec2, <3 x float> %vec3, i8 %arg, float %farg0, float %farg1, i64 %v0, i64 %v1, i32 %v2, i1 %c0, i1 %c1) {74entry:75 %gep0 = getelementptr float, ptr %ptr, i32 076 %gep1 = getelementptr float, ptr %ptr, i32 177 store float %farg0, ptr %gep178 br label %bb79 80bb:81 %gep3 = getelementptr float, ptr %ptr, i32 382 %ld0 = load float, ptr %gep083 %ld0b = load float, ptr %gep084 %ld1 = load float, ptr %gep185 %ld3 = load float, ptr %gep386 store float %ld0, ptr %gep087 store float %ld1, ptr %gep188 store <2 x float> %vec2, ptr %gep189 store <3 x float> %vec3, ptr %gep390 store i8 %arg, ptr %gep191 %fadd0 = fadd float %farg0, %farg092 %fadd1 = fadd fast float %farg1, %farg193 %trunc0 = trunc nuw nsw i64 %v0 to i894 %trunc1 = trunc nsw i64 %v1 to i895 %trunc64to8 = trunc i64 %v0 to i896 %trunc32to8 = trunc i32 %v2 to i897 %cmpSLT = icmp slt i64 %v0, %v198 %cmpSGT = icmp sgt i64 %v0, %v199 %sel0 = select i1 %c0, <2 x float> %vec2, <2 x float> %vec2100 %sel1 = select i1 %c1, <2 x float> %vec2, <2 x float> %vec2101 ret void102}103)IR");104 llvm::Function *LLVMF = &*M->getFunction("foo");105 getAnalyses(*LLVMF);106 const auto &DL = M->getDataLayout();107 108 sandboxir::Context Ctx(C);109 auto *F = Ctx.createFunction(LLVMF);110 auto *EntryBB = getBasicBlockByName(F, "entry");111 auto It = EntryBB->begin();112 [[maybe_unused]] auto *Gep0 = cast<sandboxir::GetElementPtrInst>(&*It++);113 [[maybe_unused]] auto *Gep1 = cast<sandboxir::GetElementPtrInst>(&*It++);114 auto *St1Entry = cast<sandboxir::StoreInst>(&*It++);115 116 auto *BB = getBasicBlockByName(F, "bb");117 It = BB->begin();118 [[maybe_unused]] auto *Gep3 = cast<sandboxir::GetElementPtrInst>(&*It++);119 auto *Ld0 = cast<sandboxir::LoadInst>(&*It++);120 auto *Ld0b = cast<sandboxir::LoadInst>(&*It++);121 auto *Ld1 = cast<sandboxir::LoadInst>(&*It++);122 auto *Ld3 = cast<sandboxir::LoadInst>(&*It++);123 auto *St0 = cast<sandboxir::StoreInst>(&*It++);124 auto *St1 = cast<sandboxir::StoreInst>(&*It++);125 auto *StVec2 = cast<sandboxir::StoreInst>(&*It++);126 auto *StVec3 = cast<sandboxir::StoreInst>(&*It++);127 auto *StI8 = cast<sandboxir::StoreInst>(&*It++);128 auto *FAdd0 = cast<sandboxir::BinaryOperator>(&*It++);129 auto *FAdd1 = cast<sandboxir::BinaryOperator>(&*It++);130 auto *Trunc0 = cast<sandboxir::TruncInst>(&*It++);131 auto *Trunc1 = cast<sandboxir::TruncInst>(&*It++);132 auto *Trunc64to8 = cast<sandboxir::TruncInst>(&*It++);133 auto *Trunc32to8 = cast<sandboxir::TruncInst>(&*It++);134 auto *CmpSLT = cast<sandboxir::CmpInst>(&*It++);135 auto *CmpSGT = cast<sandboxir::CmpInst>(&*It++);136 auto *Sel0 = cast<sandboxir::SelectInst>(&*It++);137 auto *Sel1 = cast<sandboxir::SelectInst>(&*It++);138 139 llvm::sandboxir::InstrMaps IMaps;140 sandboxir::LegalityAnalysis Legality(*AA, *SE, DL, Ctx, IMaps);141 const auto &Result =142 Legality.canVectorize({St0, St1}, /*SkipScheduling=*/true);143 EXPECT_TRUE(isa<sandboxir::Widen>(Result));144 145 {146 // Check NotInstructions147 auto &Result = Legality.canVectorize({F, St0}, /*SkipScheduling=*/true);148 EXPECT_TRUE(isa<sandboxir::Pack>(Result));149 EXPECT_EQ(cast<sandboxir::Pack>(Result).getReason(),150 sandboxir::ResultReason::NotInstructions);151 }152 {153 // Check DiffOpcodes154 const auto &Result =155 Legality.canVectorize({St0, Ld0}, /*SkipScheduling=*/true);156 EXPECT_TRUE(isa<sandboxir::Pack>(Result));157 EXPECT_EQ(cast<sandboxir::Pack>(Result).getReason(),158 sandboxir::ResultReason::DiffOpcodes);159 }160 {161 // Check DiffTypes162 EXPECT_TRUE(isa<sandboxir::Widen>(163 Legality.canVectorize({St0, StVec2}, /*SkipScheduling=*/true)));164 EXPECT_TRUE(isa<sandboxir::Widen>(165 Legality.canVectorize({StVec2, StVec3}, /*SkipScheduling=*/true)));166 167 const auto &Result =168 Legality.canVectorize({St0, StI8}, /*SkipScheduling=*/true);169 EXPECT_TRUE(isa<sandboxir::Pack>(Result));170 EXPECT_EQ(cast<sandboxir::Pack>(Result).getReason(),171 sandboxir::ResultReason::DiffTypes);172 }173 {174 // Check DiffMathFlags175 const auto &Result =176 Legality.canVectorize({FAdd0, FAdd1}, /*SkipScheduling=*/true);177 EXPECT_TRUE(isa<sandboxir::Pack>(Result));178 EXPECT_EQ(cast<sandboxir::Pack>(Result).getReason(),179 sandboxir::ResultReason::DiffMathFlags);180 }181 {182 // Check DiffWrapFlags183 const auto &Result =184 Legality.canVectorize({Trunc0, Trunc1}, /*SkipScheduling=*/true);185 EXPECT_TRUE(isa<sandboxir::Pack>(Result));186 EXPECT_EQ(cast<sandboxir::Pack>(Result).getReason(),187 sandboxir::ResultReason::DiffWrapFlags);188 }189 {190 // Check DiffBBs191 const auto &Result =192 Legality.canVectorize({St0, St1Entry}, /*SkipScheduling=*/true);193 EXPECT_TRUE(isa<sandboxir::Pack>(Result));194 EXPECT_EQ(cast<sandboxir::Pack>(Result).getReason(),195 sandboxir::ResultReason::DiffBBs);196 }197 {198 // Check DiffTypes for unary operands that have a different type.199 const auto &Result = Legality.canVectorize({Trunc64to8, Trunc32to8},200 /*SkipScheduling=*/true);201 EXPECT_TRUE(isa<sandboxir::Pack>(Result));202 EXPECT_EQ(cast<sandboxir::Pack>(Result).getReason(),203 sandboxir::ResultReason::DiffTypes);204 }205 {206 // Check DiffOpcodes for CMPs with different predicates.207 const auto &Result =208 Legality.canVectorize({CmpSLT, CmpSGT}, /*SkipScheduling=*/true);209 EXPECT_TRUE(isa<sandboxir::Pack>(Result));210 EXPECT_EQ(cast<sandboxir::Pack>(Result).getReason(),211 sandboxir::ResultReason::DiffOpcodes);212 }213 {214 // Check NotConsecutive Ld0,Ld0b215 const auto &Result =216 Legality.canVectorize({Ld0, Ld0b}, /*SkipScheduling=*/true);217 EXPECT_TRUE(isa<sandboxir::Pack>(Result));218 EXPECT_EQ(cast<sandboxir::Pack>(Result).getReason(),219 sandboxir::ResultReason::NotConsecutive);220 }221 {222 // Check NotConsecutive Ld0,Ld3223 const auto &Result =224 Legality.canVectorize({Ld0, Ld3}, /*SkipScheduling=*/true);225 EXPECT_TRUE(isa<sandboxir::Pack>(Result));226 EXPECT_EQ(cast<sandboxir::Pack>(Result).getReason(),227 sandboxir::ResultReason::NotConsecutive);228 }229 {230 // Check Widen Ld0,Ld1231 const auto &Result =232 Legality.canVectorize({Ld0, Ld1}, /*SkipScheduling=*/true);233 EXPECT_TRUE(isa<sandboxir::Widen>(Result));234 }235 {236 // Check Repeated instructions (splat)237 const auto &Result =238 Legality.canVectorize({Ld0, Ld0}, /*SkipScheduling=*/true);239 EXPECT_TRUE(isa<sandboxir::Pack>(Result));240 EXPECT_EQ(cast<sandboxir::Pack>(Result).getReason(),241 sandboxir::ResultReason::RepeatedInstrs);242 }243 {244 // Check Repeated instructions (not splat)245 const auto &Result =246 Legality.canVectorize({Ld0, Ld1, Ld0}, /*SkipScheduling=*/true);247 EXPECT_TRUE(isa<sandboxir::Pack>(Result));248 EXPECT_EQ(cast<sandboxir::Pack>(Result).getReason(),249 sandboxir::ResultReason::RepeatedInstrs);250 }251 {252 // For now don't vectorize Selects when the number of elements of conditions253 // doesn't match the operands.254 const auto &Result =255 Legality.canVectorize({Sel0, Sel1}, /*SkipScheduling=*/true);256 EXPECT_TRUE(isa<sandboxir::Pack>(Result));257 EXPECT_EQ(cast<sandboxir::Pack>(Result).getReason(),258 sandboxir::ResultReason::Unimplemented);259 }260}261 262TEST_F(LegalityTest, LegalitySchedule) {263 parseIR(C, R"IR(264define void @foo(ptr %ptr) {265 %gep0 = getelementptr float, ptr %ptr, i32 0266 %gep1 = getelementptr float, ptr %ptr, i32 1267 %ld0 = load float, ptr %gep0268 store float %ld0, ptr %gep1269 %ld1 = load float, ptr %gep1270 store float %ld0, ptr %gep0271 store float %ld1, ptr %gep1272 ret void273}274)IR");275 llvm::Function *LLVMF = &*M->getFunction("foo");276 getAnalyses(*LLVMF);277 const auto &DL = M->getDataLayout();278 279 sandboxir::Context Ctx(C);280 auto *F = Ctx.createFunction(LLVMF);281 auto *BB = &*F->begin();282 auto It = BB->begin();283 [[maybe_unused]] auto *Gep0 = cast<sandboxir::GetElementPtrInst>(&*It++);284 [[maybe_unused]] auto *Gep1 = cast<sandboxir::GetElementPtrInst>(&*It++);285 auto *Ld0 = cast<sandboxir::LoadInst>(&*It++);286 [[maybe_unused]] auto *ConflictingSt = cast<sandboxir::StoreInst>(&*It++);287 auto *Ld1 = cast<sandboxir::LoadInst>(&*It++);288 auto *St0 = cast<sandboxir::StoreInst>(&*It++);289 auto *St1 = cast<sandboxir::StoreInst>(&*It++);290 291 llvm::sandboxir::InstrMaps IMaps;292 sandboxir::LegalityAnalysis Legality(*AA, *SE, DL, Ctx, IMaps);293 {294 // Can vectorize St0,St1.295 const auto &Result = Legality.canVectorize({St0, St1});296 EXPECT_TRUE(isa<sandboxir::Widen>(Result));297 }298 {299 // Can't vectorize Ld0,Ld1 because of conflicting store.300 auto &Result = Legality.canVectorize({Ld0, Ld1});301 EXPECT_TRUE(isa<sandboxir::Pack>(Result));302 EXPECT_EQ(cast<sandboxir::Pack>(Result).getReason(),303 sandboxir::ResultReason::CantSchedule);304 }305}306 307#ifndef NDEBUG308TEST_F(LegalityTest, LegalityResultDump) {309 parseIR(C, R"IR(310define void @foo() {311 ret void312}313)IR");314 llvm::Function *LLVMF = &*M->getFunction("foo");315 getAnalyses(*LLVMF);316 const auto &DL = M->getDataLayout();317 318 auto Matches = [](const sandboxir::LegalityResult &Result,319 const std::string &ExpectedStr) -> bool {320 std::string Buff;321 raw_string_ostream OS(Buff);322 Result.print(OS);323 return Buff == ExpectedStr;324 };325 326 sandboxir::Context Ctx(C);327 llvm::sandboxir::InstrMaps IMaps;328 sandboxir::LegalityAnalysis Legality(*AA, *SE, DL, Ctx, IMaps);329 EXPECT_TRUE(330 Matches(Legality.createLegalityResult<sandboxir::Widen>(), "Widen"));331 EXPECT_TRUE(Matches(Legality.createLegalityResult<sandboxir::Pack>(332 sandboxir::ResultReason::NotInstructions),333 "Pack Reason: NotInstructions"));334 EXPECT_TRUE(Matches(Legality.createLegalityResult<sandboxir::Pack>(335 sandboxir::ResultReason::DiffOpcodes),336 "Pack Reason: DiffOpcodes"));337 EXPECT_TRUE(Matches(Legality.createLegalityResult<sandboxir::Pack>(338 sandboxir::ResultReason::DiffTypes),339 "Pack Reason: DiffTypes"));340 EXPECT_TRUE(Matches(Legality.createLegalityResult<sandboxir::Pack>(341 sandboxir::ResultReason::DiffMathFlags),342 "Pack Reason: DiffMathFlags"));343 EXPECT_TRUE(Matches(Legality.createLegalityResult<sandboxir::Pack>(344 sandboxir::ResultReason::DiffWrapFlags),345 "Pack Reason: DiffWrapFlags"));346}347#endif // NDEBUG348 349TEST_F(LegalityTest, CollectDescr) {350 parseIR(C, R"IR(351define void @foo(ptr %ptr) {352 %gep0 = getelementptr float, ptr %ptr, i32 0353 %gep1 = getelementptr float, ptr %ptr, i32 1354 %ld0 = load float, ptr %gep0355 %ld1 = load float, ptr %gep1356 %vld = load <4 x float>, ptr %ptr357 ret void358}359)IR");360 llvm::Function *LLVMF = &*M->getFunction("foo");361 getAnalyses(*LLVMF);362 sandboxir::Context Ctx(C);363 auto *F = Ctx.createFunction(LLVMF);364 auto *BB = &*F->begin();365 auto It = BB->begin();366 [[maybe_unused]] auto *Gep0 = cast<sandboxir::GetElementPtrInst>(&*It++);367 [[maybe_unused]] auto *Gep1 = cast<sandboxir::GetElementPtrInst>(&*It++);368 auto *Ld0 = cast<sandboxir::LoadInst>(&*It++);369 [[maybe_unused]] auto *Ld1 = cast<sandboxir::LoadInst>(&*It++);370 auto *VLd = cast<sandboxir::LoadInst>(&*It++);371 372 sandboxir::CollectDescr::DescrVecT Descrs;373 using EEDescr = sandboxir::CollectDescr::ExtractElementDescr;374 SmallVector<sandboxir::Value *> Bndl({VLd});375 SmallVector<sandboxir::Value *> UB;376 sandboxir::Action VLdA(nullptr, Bndl, UB, 0);377 {378 // Check single input, no shuffle.379 Descrs.push_back(EEDescr(&VLdA, 0));380 Descrs.push_back(EEDescr(&VLdA, 1));381 sandboxir::CollectDescr CD(std::move(Descrs));382 EXPECT_TRUE(CD.getSingleInput());383 EXPECT_EQ(CD.getSingleInput()->first, &VLdA);384 EXPECT_THAT(CD.getSingleInput()->second, testing::ElementsAre(0, 1));385 EXPECT_TRUE(CD.hasVectorInputs());386 }387 {388 // Check single input, shuffle.389 Descrs.push_back(EEDescr(&VLdA, 1));390 Descrs.push_back(EEDescr(&VLdA, 0));391 sandboxir::CollectDescr CD(std::move(Descrs));392 EXPECT_TRUE(CD.getSingleInput());393 EXPECT_EQ(CD.getSingleInput()->first, &VLdA);394 EXPECT_THAT(CD.getSingleInput()->second, testing::ElementsAre(1, 0));395 EXPECT_TRUE(CD.hasVectorInputs());396 }397 {398 // Check multiple inputs.399 Descrs.push_back(EEDescr(Ld0));400 Descrs.push_back(EEDescr(&VLdA, 0));401 Descrs.push_back(EEDescr(&VLdA, 1));402 sandboxir::CollectDescr CD(std::move(Descrs));403 EXPECT_FALSE(CD.getSingleInput());404 EXPECT_TRUE(CD.hasVectorInputs());405 }406 {407 // Check multiple inputs only scalars.408 Descrs.push_back(EEDescr(Ld0));409 Descrs.push_back(EEDescr(Ld1));410 sandboxir::CollectDescr CD(std::move(Descrs));411 EXPECT_FALSE(CD.getSingleInput());412 EXPECT_FALSE(CD.hasVectorInputs());413 }414}415 416TEST_F(LegalityTest, ShuffleMask) {417 {418 // Check SmallVector constructor.419 SmallVector<int> Indices({0, 1, 2, 3});420 sandboxir::ShuffleMask Mask(std::move(Indices));421 EXPECT_THAT(Mask, testing::ElementsAre(0, 1, 2, 3));422 }423 {424 // Check initializer_list constructor.425 sandboxir::ShuffleMask Mask({0, 1, 2, 3});426 EXPECT_THAT(Mask, testing::ElementsAre(0, 1, 2, 3));427 }428 {429 // Check ArrayRef constructor.430 sandboxir::ShuffleMask Mask(ArrayRef<int>({0, 1, 2, 3}));431 EXPECT_THAT(Mask, testing::ElementsAre(0, 1, 2, 3));432 }433 {434 // Check operator ArrayRef<int>().435 sandboxir::ShuffleMask Mask({0, 1, 2, 3});436 ArrayRef<int> Array = Mask;437 EXPECT_THAT(Array, testing::ElementsAre(0, 1, 2, 3));438 }439 {440 // Check getIdentity().441 auto IdentityMask = sandboxir::ShuffleMask::getIdentity(4);442 EXPECT_THAT(IdentityMask, testing::ElementsAre(0, 1, 2, 3));443 EXPECT_TRUE(IdentityMask.isIdentity());444 }445 {446 // Check isIdentity().447 sandboxir::ShuffleMask Mask1({0, 1, 2, 3});448 EXPECT_TRUE(Mask1.isIdentity());449 sandboxir::ShuffleMask Mask2({1, 2, 3, 4});450 EXPECT_FALSE(Mask2.isIdentity());451 }452 {453 // Check operator==().454 sandboxir::ShuffleMask Mask1({0, 1, 2, 3});455 sandboxir::ShuffleMask Mask2({0, 1, 2, 3});456 EXPECT_TRUE(Mask1 == Mask2);457 EXPECT_FALSE(Mask1 != Mask2);458 }459 {460 // Check operator!=().461 sandboxir::ShuffleMask Mask1({0, 1, 2, 3});462 sandboxir::ShuffleMask Mask2({0, 1, 2, 4});463 EXPECT_TRUE(Mask1 != Mask2);464 EXPECT_FALSE(Mask1 == Mask2);465 }466 {467 // Check size().468 sandboxir::ShuffleMask Mask({0, 1, 2, 3});469 EXPECT_EQ(Mask.size(), 4u);470 }471 {472 // Check operator[].473 sandboxir::ShuffleMask Mask({0, 1, 2, 3});474 for (auto [Idx, Elm] : enumerate(Mask)) {475 EXPECT_EQ(Elm, Mask[Idx]);476 }477 }478 {479 // Check begin(), end().480 sandboxir::ShuffleMask Mask({0, 1, 2, 3});481 sandboxir::ShuffleMask::const_iterator Begin = Mask.begin();482 sandboxir::ShuffleMask::const_iterator End = Mask.begin();483 int Idx = 0;484 for (auto It = Begin; It != End; ++It) {485 EXPECT_EQ(*It, Mask[Idx++]);486 }487 }488#ifndef NDEBUG489 {490 // Check print(OS).491 sandboxir::ShuffleMask Mask({0, 1, 2, 3});492 std::string Str;493 raw_string_ostream OS(Str);494 Mask.print(OS);495 EXPECT_EQ(Str, "0,1,2,3");496 }497 {498 // Check operator<<().499 sandboxir::ShuffleMask Mask({0, 1, 2, 3});500 std::string Str;501 raw_string_ostream OS(Str);502 OS << Mask;503 EXPECT_EQ(Str, "0,1,2,3");504 }505#endif // NDEBUG506}507