Path: blob/main/contrib/llvm-project/llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp
35266 views
//===- VPlanAnalysis.cpp - Various Analyses working on VPlan ----*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "VPlanAnalysis.h"9#include "VPlan.h"10#include "VPlanCFG.h"11#include "llvm/ADT/TypeSwitch.h"12#include "llvm/IR/Instruction.h"13#include "llvm/IR/PatternMatch.h"1415using namespace llvm;1617#define DEBUG_TYPE "vplan"1819Type *VPTypeAnalysis::inferScalarTypeForRecipe(const VPBlendRecipe *R) {20Type *ResTy = inferScalarType(R->getIncomingValue(0));21for (unsigned I = 1, E = R->getNumIncomingValues(); I != E; ++I) {22VPValue *Inc = R->getIncomingValue(I);23assert(inferScalarType(Inc) == ResTy &&24"different types inferred for different incoming values");25CachedTypes[Inc] = ResTy;26}27return ResTy;28}2930Type *VPTypeAnalysis::inferScalarTypeForRecipe(const VPInstruction *R) {31// Set the result type from the first operand, check if the types for all32// other operands match and cache them.33auto SetResultTyFromOp = [this, R]() {34Type *ResTy = inferScalarType(R->getOperand(0));35for (unsigned Op = 1; Op != R->getNumOperands(); ++Op) {36VPValue *OtherV = R->getOperand(Op);37assert(inferScalarType(OtherV) == ResTy &&38"different types inferred for different operands");39CachedTypes[OtherV] = ResTy;40}41return ResTy;42};4344unsigned Opcode = R->getOpcode();45if (Instruction::isBinaryOp(Opcode) || Instruction::isUnaryOp(Opcode))46return SetResultTyFromOp();4748switch (Opcode) {49case Instruction::Select: {50Type *ResTy = inferScalarType(R->getOperand(1));51VPValue *OtherV = R->getOperand(2);52assert(inferScalarType(OtherV) == ResTy &&53"different types inferred for different operands");54CachedTypes[OtherV] = ResTy;55return ResTy;56}57case Instruction::ICmp:58case VPInstruction::ActiveLaneMask:59return inferScalarType(R->getOperand(1));60case VPInstruction::FirstOrderRecurrenceSplice:61case VPInstruction::Not:62return SetResultTyFromOp();63case VPInstruction::ExtractFromEnd: {64Type *BaseTy = inferScalarType(R->getOperand(0));65if (auto *VecTy = dyn_cast<VectorType>(BaseTy))66return VecTy->getElementType();67return BaseTy;68}69case VPInstruction::LogicalAnd:70return IntegerType::get(Ctx, 1);71case VPInstruction::PtrAdd:72// Return the type based on the pointer argument (i.e. first operand).73return inferScalarType(R->getOperand(0));74case VPInstruction::BranchOnCond:75case VPInstruction::BranchOnCount:76return Type::getVoidTy(Ctx);77default:78break;79}80// Type inference not implemented for opcode.81LLVM_DEBUG({82dbgs() << "LV: Found unhandled opcode for: ";83R->getVPSingleValue()->dump();84});85llvm_unreachable("Unhandled opcode!");86}8788Type *VPTypeAnalysis::inferScalarTypeForRecipe(const VPWidenRecipe *R) {89unsigned Opcode = R->getOpcode();90switch (Opcode) {91case Instruction::ICmp:92case Instruction::FCmp:93return IntegerType::get(Ctx, 1);94case Instruction::UDiv:95case Instruction::SDiv:96case Instruction::SRem:97case Instruction::URem:98case Instruction::Add:99case Instruction::FAdd:100case Instruction::Sub:101case Instruction::FSub:102case Instruction::Mul:103case Instruction::FMul:104case Instruction::FDiv:105case Instruction::FRem:106case Instruction::Shl:107case Instruction::LShr:108case Instruction::AShr:109case Instruction::And:110case Instruction::Or:111case Instruction::Xor: {112Type *ResTy = inferScalarType(R->getOperand(0));113assert(ResTy == inferScalarType(R->getOperand(1)) &&114"types for both operands must match for binary op");115CachedTypes[R->getOperand(1)] = ResTy;116return ResTy;117}118case Instruction::FNeg:119case Instruction::Freeze:120return inferScalarType(R->getOperand(0));121default:122break;123}124125// Type inference not implemented for opcode.126LLVM_DEBUG({127dbgs() << "LV: Found unhandled opcode for: ";128R->getVPSingleValue()->dump();129});130llvm_unreachable("Unhandled opcode!");131}132133Type *VPTypeAnalysis::inferScalarTypeForRecipe(const VPWidenCallRecipe *R) {134auto &CI = *cast<CallInst>(R->getUnderlyingInstr());135return CI.getType();136}137138Type *VPTypeAnalysis::inferScalarTypeForRecipe(const VPWidenMemoryRecipe *R) {139assert((isa<VPWidenLoadRecipe>(R) || isa<VPWidenLoadEVLRecipe>(R)) &&140"Store recipes should not define any values");141return cast<LoadInst>(&R->getIngredient())->getType();142}143144Type *VPTypeAnalysis::inferScalarTypeForRecipe(const VPWidenSelectRecipe *R) {145Type *ResTy = inferScalarType(R->getOperand(1));146VPValue *OtherV = R->getOperand(2);147assert(inferScalarType(OtherV) == ResTy &&148"different types inferred for different operands");149CachedTypes[OtherV] = ResTy;150return ResTy;151}152153Type *VPTypeAnalysis::inferScalarTypeForRecipe(const VPReplicateRecipe *R) {154switch (R->getUnderlyingInstr()->getOpcode()) {155case Instruction::Call: {156unsigned CallIdx = R->getNumOperands() - (R->isPredicated() ? 2 : 1);157return cast<Function>(R->getOperand(CallIdx)->getLiveInIRValue())158->getReturnType();159}160case Instruction::UDiv:161case Instruction::SDiv:162case Instruction::SRem:163case Instruction::URem:164case Instruction::Add:165case Instruction::FAdd:166case Instruction::Sub:167case Instruction::FSub:168case Instruction::Mul:169case Instruction::FMul:170case Instruction::FDiv:171case Instruction::FRem:172case Instruction::Shl:173case Instruction::LShr:174case Instruction::AShr:175case Instruction::And:176case Instruction::Or:177case Instruction::Xor: {178Type *ResTy = inferScalarType(R->getOperand(0));179assert(ResTy == inferScalarType(R->getOperand(1)) &&180"inferred types for operands of binary op don't match");181CachedTypes[R->getOperand(1)] = ResTy;182return ResTy;183}184case Instruction::Select: {185Type *ResTy = inferScalarType(R->getOperand(1));186assert(ResTy == inferScalarType(R->getOperand(2)) &&187"inferred types for operands of select op don't match");188CachedTypes[R->getOperand(2)] = ResTy;189return ResTy;190}191case Instruction::ICmp:192case Instruction::FCmp:193return IntegerType::get(Ctx, 1);194case Instruction::AddrSpaceCast:195case Instruction::Alloca:196case Instruction::BitCast:197case Instruction::Trunc:198case Instruction::SExt:199case Instruction::ZExt:200case Instruction::FPExt:201case Instruction::FPTrunc:202case Instruction::ExtractValue:203case Instruction::SIToFP:204case Instruction::UIToFP:205case Instruction::FPToSI:206case Instruction::FPToUI:207case Instruction::PtrToInt:208case Instruction::IntToPtr:209return R->getUnderlyingInstr()->getType();210case Instruction::Freeze:211case Instruction::FNeg:212case Instruction::GetElementPtr:213return inferScalarType(R->getOperand(0));214case Instruction::Load:215return cast<LoadInst>(R->getUnderlyingInstr())->getType();216case Instruction::Store:217// FIXME: VPReplicateRecipes with store opcodes still define a result218// VPValue, so we need to handle them here. Remove the code here once this219// is modeled accurately in VPlan.220return Type::getVoidTy(Ctx);221default:222break;223}224// Type inference not implemented for opcode.225LLVM_DEBUG({226dbgs() << "LV: Found unhandled opcode for: ";227R->getVPSingleValue()->dump();228});229llvm_unreachable("Unhandled opcode");230}231232Type *VPTypeAnalysis::inferScalarType(const VPValue *V) {233if (Type *CachedTy = CachedTypes.lookup(V))234return CachedTy;235236if (V->isLiveIn()) {237if (auto *IRValue = V->getLiveInIRValue())238return IRValue->getType();239// All VPValues without any underlying IR value (like the vector trip count240// or the backedge-taken count) have the same type as the canonical IV.241return CanonicalIVTy;242}243244Type *ResultTy =245TypeSwitch<const VPRecipeBase *, Type *>(V->getDefiningRecipe())246.Case<VPActiveLaneMaskPHIRecipe, VPCanonicalIVPHIRecipe,247VPFirstOrderRecurrencePHIRecipe, VPReductionPHIRecipe,248VPWidenPointerInductionRecipe, VPEVLBasedIVPHIRecipe>(249[this](const auto *R) {250// Handle header phi recipes, except VPWidenIntOrFpInduction251// which needs special handling due it being possibly truncated.252// TODO: consider inferring/caching type of siblings, e.g.,253// backedge value, here and in cases below.254return inferScalarType(R->getStartValue());255})256.Case<VPWidenIntOrFpInductionRecipe, VPDerivedIVRecipe>(257[](const auto *R) { return R->getScalarType(); })258.Case<VPReductionRecipe, VPPredInstPHIRecipe, VPWidenPHIRecipe,259VPScalarIVStepsRecipe, VPWidenGEPRecipe, VPVectorPointerRecipe,260VPWidenCanonicalIVRecipe>([this](const VPRecipeBase *R) {261return inferScalarType(R->getOperand(0));262})263.Case<VPBlendRecipe, VPInstruction, VPWidenRecipe, VPReplicateRecipe,264VPWidenCallRecipe, VPWidenMemoryRecipe, VPWidenSelectRecipe>(265[this](const auto *R) { return inferScalarTypeForRecipe(R); })266.Case<VPInterleaveRecipe>([V](const VPInterleaveRecipe *R) {267// TODO: Use info from interleave group.268return V->getUnderlyingValue()->getType();269})270.Case<VPWidenCastRecipe>(271[](const VPWidenCastRecipe *R) { return R->getResultType(); })272.Case<VPScalarCastRecipe>(273[](const VPScalarCastRecipe *R) { return R->getResultType(); })274.Case<VPExpandSCEVRecipe>([](const VPExpandSCEVRecipe *R) {275return R->getSCEV()->getType();276})277.Case<VPReductionRecipe>([this](const auto *R) {278return inferScalarType(R->getChainOp());279});280281assert(ResultTy && "could not infer type for the given VPValue");282CachedTypes[V] = ResultTy;283return ResultTy;284}285286void llvm::collectEphemeralRecipesForVPlan(287VPlan &Plan, DenseSet<VPRecipeBase *> &EphRecipes) {288// First, collect seed recipes which are operands of assumes.289SmallVector<VPRecipeBase *> Worklist;290for (VPBasicBlock *VPBB : VPBlockUtils::blocksOnly<VPBasicBlock>(291vp_depth_first_deep(Plan.getVectorLoopRegion()->getEntry()))) {292for (VPRecipeBase &R : *VPBB) {293auto *RepR = dyn_cast<VPReplicateRecipe>(&R);294if (!RepR || !match(RepR->getUnderlyingInstr(),295PatternMatch::m_Intrinsic<Intrinsic::assume>()))296continue;297Worklist.push_back(RepR);298EphRecipes.insert(RepR);299}300}301302// Process operands of candidates in worklist and add them to the set of303// ephemeral recipes, if they don't have side-effects and are only used by304// other ephemeral recipes.305while (!Worklist.empty()) {306VPRecipeBase *Cur = Worklist.pop_back_val();307for (VPValue *Op : Cur->operands()) {308auto *OpR = Op->getDefiningRecipe();309if (!OpR || OpR->mayHaveSideEffects() || EphRecipes.contains(OpR))310continue;311if (any_of(Op->users(), [EphRecipes](VPUser *U) {312auto *UR = dyn_cast<VPRecipeBase>(U);313return !UR || !EphRecipes.contains(UR);314}))315continue;316EphRecipes.insert(OpR);317Worklist.push_back(OpR);318}319}320}321322323