Skip to content

Commit b0b8864

Browse files
authored
[VPlan] Add initial anlysis to infer scalar type of VPValues. (#69013)
This patch adds initial type inferrence for VPValues. It infers the scalar type of a VPValue, by bottom-up traversing through defining recipes until root nodes with known types are reached (e.g. live-ins or load recipes). The types are then propagated top down through operations. This is intended as building block for a VPlan-based cost model, which will need access to type information for VPValues/recipes. Initial testing is done by asserting the inferred type matches the type of the result value generated for a widen and replicate recipes.
1 parent 37d9dc4 commit b0b8864

File tree

6 files changed

+333
-7
lines changed

6 files changed

+333
-7
lines changed

llvm/lib/Transforms/Vectorize/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ add_llvm_component_library(LLVMVectorize
66
Vectorize.cpp
77
VectorCombine.cpp
88
VPlan.cpp
9+
VPlanAnalysis.cpp
910
VPlanHCFGBuilder.cpp
1011
VPlanRecipes.cpp
1112
VPlanSLP.cpp

llvm/lib/Transforms/Vectorize/LoopVectorize.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
#include "LoopVectorizationPlanner.h"
5858
#include "VPRecipeBuilder.h"
5959
#include "VPlan.h"
60+
#include "VPlanAnalysis.h"
6061
#include "VPlanHCFGBuilder.h"
6162
#include "VPlanTransforms.h"
6263
#include "llvm/ADT/APInt.h"
@@ -2702,8 +2703,15 @@ void InnerLoopVectorizer::scalarizeInstruction(const Instruction *Instr,
27022703
bool IsVoidRetTy = Instr->getType()->isVoidTy();
27032704

27042705
Instruction *Cloned = Instr->clone();
2705-
if (!IsVoidRetTy)
2706+
if (!IsVoidRetTy) {
27062707
Cloned->setName(Instr->getName() + ".cloned");
2708+
#if !defined(NDEBUG)
2709+
// Verify that VPlan type inference results agree with the type of the
2710+
// generated values.
2711+
assert(State.TypeAnalysis.inferScalarType(RepRecipe) == Cloned->getType() &&
2712+
"inferred type and type from generated instructions do not match");
2713+
#endif
2714+
}
27072715

27082716
RepRecipe->setFlags(Cloned);
27092717

@@ -7660,7 +7668,8 @@ SCEV2ValueTy LoopVectorizationPlanner::executePlan(
76607668
VPlanTransforms::optimizeForVFAndUF(BestVPlan, BestVF, BestUF, PSE);
76617669

76627670
// Perform the actual loop transformation.
7663-
VPTransformState State{BestVF, BestUF, LI, DT, ILV.Builder, &ILV, &BestVPlan};
7671+
VPTransformState State(BestVF, BestUF, LI, DT, ILV.Builder, &ILV, &BestVPlan,
7672+
OrigLoop->getHeader()->getContext());
76647673

76657674
// 0. Generate SCEV-dependent code into the preheader, including TripCount,
76667675
// before making any changes to the CFG.

llvm/lib/Transforms/Vectorize/VPlan.h

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#ifndef LLVM_TRANSFORMS_VECTORIZE_VPLAN_H
2424
#define LLVM_TRANSFORMS_VECTORIZE_VPLAN_H
2525

26+
#include "VPlanAnalysis.h"
2627
#include "VPlanValue.h"
2728
#include "llvm/ADT/DenseMap.h"
2829
#include "llvm/ADT/MapVector.h"
@@ -233,9 +234,9 @@ struct VPIteration {
233234
struct VPTransformState {
234235
VPTransformState(ElementCount VF, unsigned UF, LoopInfo *LI,
235236
DominatorTree *DT, IRBuilderBase &Builder,
236-
InnerLoopVectorizer *ILV, VPlan *Plan)
237+
InnerLoopVectorizer *ILV, VPlan *Plan, LLVMContext &Ctx)
237238
: VF(VF), UF(UF), LI(LI), DT(DT), Builder(Builder), ILV(ILV), Plan(Plan),
238-
LVer(nullptr) {}
239+
LVer(nullptr), TypeAnalysis(Ctx) {}
239240

240241
/// The chosen Vectorization and Unroll Factors of the loop being vectorized.
241242
ElementCount VF;
@@ -413,6 +414,9 @@ struct VPTransformState {
413414
/// Map SCEVs to their expanded values. Populated when executing
414415
/// VPExpandSCEVRecipes.
415416
DenseMap<const SCEV *, Value *> ExpandedSCEVs;
417+
418+
/// VPlan-based type analysis.
419+
VPTypeAnalysis TypeAnalysis;
416420
};
417421

418422
/// VPBlockBase is the building block of the Hierarchical Control-Flow Graph.
@@ -1167,6 +1171,8 @@ class VPWidenRecipe : public VPRecipeWithIRFlags, public VPValue {
11671171
/// Produce widened copies of all Ingredients.
11681172
void execute(VPTransformState &State) override;
11691173

1174+
unsigned getOpcode() const { return Opcode; }
1175+
11701176
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
11711177
/// Print the recipe.
11721178
void print(raw_ostream &O, const Twine &Indent,
@@ -1458,7 +1464,7 @@ class VPWidenIntOrFpInductionRecipe : public VPHeaderPHIRecipe {
14581464
bool isCanonical() const;
14591465

14601466
/// Returns the scalar type of the induction.
1461-
const Type *getScalarType() const {
1467+
Type *getScalarType() const {
14621468
return Trunc ? Trunc->getType() : IV->getType();
14631469
}
14641470
};
@@ -2080,8 +2086,8 @@ class VPCanonicalIVPHIRecipe : public VPHeaderPHIRecipe {
20802086
#endif
20812087

20822088
/// Returns the scalar type of the induction.
2083-
const Type *getScalarType() const {
2084-
return getOperand(0)->getLiveInIRValue()->getType();
2089+
Type *getScalarType() const {
2090+
return getStartValue()->getLiveInIRValue()->getType();
20852091
}
20862092

20872093
/// Returns true if the recipe only uses the first lane of operand \p Op.
@@ -2192,6 +2198,11 @@ class VPDerivedIVRecipe : public VPRecipeBase, public VPValue {
21922198
VPSlotTracker &SlotTracker) const override;
21932199
#endif
21942200

2201+
Type *getScalarType() const {
2202+
return TruncResultTy ? TruncResultTy
2203+
: getStartValue()->getLiveInIRValue()->getType();
2204+
}
2205+
21952206
VPValue *getStartValue() const { return getOperand(0); }
21962207
VPValue *getCanonicalIV() const { return getOperand(1); }
21972208
VPValue *getStepValue() const { return getOperand(2); }
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
//===- VPlanAnalysis.cpp - Various Analyses working on VPlan ----*- C++ -*-===//
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-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "VPlanAnalysis.h"
10+
#include "VPlan.h"
11+
#include "llvm/ADT/TypeSwitch.h"
12+
13+
using namespace llvm;
14+
15+
#define DEBUG_TYPE "vplan"
16+
17+
Type *VPTypeAnalysis::inferScalarTypeForRecipe(const VPBlendRecipe *R) {
18+
Type *ResTy = inferScalarType(R->getIncomingValue(0));
19+
for (unsigned I = 1, E = R->getNumIncomingValues(); I != E; ++I) {
20+
VPValue *Inc = R->getIncomingValue(I);
21+
assert(inferScalarType(Inc) == ResTy &&
22+
"different types inferred for different incoming values");
23+
CachedTypes[Inc] = ResTy;
24+
}
25+
return ResTy;
26+
}
27+
28+
Type *VPTypeAnalysis::inferScalarTypeForRecipe(const VPInstruction *R) {
29+
switch (R->getOpcode()) {
30+
case Instruction::Select: {
31+
Type *ResTy = inferScalarType(R->getOperand(1));
32+
VPValue *OtherV = R->getOperand(2);
33+
assert(inferScalarType(OtherV) == ResTy &&
34+
"different types inferred for different operands");
35+
CachedTypes[OtherV] = ResTy;
36+
return ResTy;
37+
}
38+
case VPInstruction::FirstOrderRecurrenceSplice: {
39+
Type *ResTy = inferScalarType(R->getOperand(0));
40+
VPValue *OtherV = R->getOperand(1);
41+
assert(inferScalarType(OtherV) == ResTy &&
42+
"different types inferred for different operands");
43+
CachedTypes[OtherV] = ResTy;
44+
return ResTy;
45+
}
46+
default:
47+
break;
48+
}
49+
// Type inference not implemented for opcode.
50+
LLVM_DEBUG({
51+
dbgs() << "LV: Found unhandled opcode for: ";
52+
R->getVPSingleValue()->dump();
53+
});
54+
llvm_unreachable("Unhandled opcode!");
55+
}
56+
57+
Type *VPTypeAnalysis::inferScalarTypeForRecipe(const VPWidenRecipe *R) {
58+
unsigned Opcode = R->getOpcode();
59+
switch (Opcode) {
60+
case Instruction::ICmp:
61+
case Instruction::FCmp:
62+
return IntegerType::get(Ctx, 1);
63+
case Instruction::UDiv:
64+
case Instruction::SDiv:
65+
case Instruction::SRem:
66+
case Instruction::URem:
67+
case Instruction::Add:
68+
case Instruction::FAdd:
69+
case Instruction::Sub:
70+
case Instruction::FSub:
71+
case Instruction::Mul:
72+
case Instruction::FMul:
73+
case Instruction::FDiv:
74+
case Instruction::FRem:
75+
case Instruction::Shl:
76+
case Instruction::LShr:
77+
case Instruction::AShr:
78+
case Instruction::And:
79+
case Instruction::Or:
80+
case Instruction::Xor: {
81+
Type *ResTy = inferScalarType(R->getOperand(0));
82+
assert(ResTy == inferScalarType(R->getOperand(1)) &&
83+
"types for both operands must match for binary op");
84+
CachedTypes[R->getOperand(1)] = ResTy;
85+
return ResTy;
86+
}
87+
case Instruction::FNeg:
88+
case Instruction::Freeze:
89+
return inferScalarType(R->getOperand(0));
90+
default:
91+
break;
92+
}
93+
94+
// Type inference not implemented for opcode.
95+
LLVM_DEBUG({
96+
dbgs() << "LV: Found unhandled opcode for: ";
97+
R->getVPSingleValue()->dump();
98+
});
99+
llvm_unreachable("Unhandled opcode!");
100+
}
101+
102+
Type *VPTypeAnalysis::inferScalarTypeForRecipe(const VPWidenCallRecipe *R) {
103+
auto &CI = *cast<CallInst>(R->getUnderlyingInstr());
104+
return CI.getType();
105+
}
106+
107+
Type *VPTypeAnalysis::inferScalarTypeForRecipe(
108+
const VPWidenMemoryInstructionRecipe *R) {
109+
assert(!R->isStore() && "Store recipes should not define any values");
110+
return cast<LoadInst>(&R->getIngredient())->getType();
111+
}
112+
113+
Type *VPTypeAnalysis::inferScalarTypeForRecipe(const VPWidenSelectRecipe *R) {
114+
Type *ResTy = inferScalarType(R->getOperand(1));
115+
VPValue *OtherV = R->getOperand(2);
116+
assert(inferScalarType(OtherV) == ResTy &&
117+
"different types inferred for different operands");
118+
CachedTypes[OtherV] = ResTy;
119+
return ResTy;
120+
}
121+
122+
Type *VPTypeAnalysis::inferScalarTypeForRecipe(const VPReplicateRecipe *R) {
123+
switch (R->getUnderlyingInstr()->getOpcode()) {
124+
case Instruction::Call: {
125+
unsigned CallIdx = R->getNumOperands() - (R->isPredicated() ? 2 : 1);
126+
return cast<Function>(R->getOperand(CallIdx)->getLiveInIRValue())
127+
->getReturnType();
128+
}
129+
case Instruction::UDiv:
130+
case Instruction::SDiv:
131+
case Instruction::SRem:
132+
case Instruction::URem:
133+
case Instruction::Add:
134+
case Instruction::FAdd:
135+
case Instruction::Sub:
136+
case Instruction::FSub:
137+
case Instruction::Mul:
138+
case Instruction::FMul:
139+
case Instruction::FDiv:
140+
case Instruction::FRem:
141+
case Instruction::Shl:
142+
case Instruction::LShr:
143+
case Instruction::AShr:
144+
case Instruction::And:
145+
case Instruction::Or:
146+
case Instruction::Xor: {
147+
Type *ResTy = inferScalarType(R->getOperand(0));
148+
assert(ResTy == inferScalarType(R->getOperand(1)) &&
149+
"inferred types for operands of binary op don't match");
150+
CachedTypes[R->getOperand(1)] = ResTy;
151+
return ResTy;
152+
}
153+
case Instruction::Select: {
154+
Type *ResTy = inferScalarType(R->getOperand(1));
155+
assert(ResTy == inferScalarType(R->getOperand(2)) &&
156+
"inferred types for operands of select op don't match");
157+
CachedTypes[R->getOperand(2)] = ResTy;
158+
return ResTy;
159+
}
160+
case Instruction::ICmp:
161+
case Instruction::FCmp:
162+
return IntegerType::get(Ctx, 1);
163+
case Instruction::Alloca:
164+
case Instruction::BitCast:
165+
case Instruction::Trunc:
166+
case Instruction::SExt:
167+
case Instruction::ZExt:
168+
case Instruction::FPExt:
169+
case Instruction::FPTrunc:
170+
case Instruction::ExtractValue:
171+
case Instruction::SIToFP:
172+
case Instruction::UIToFP:
173+
case Instruction::FPToSI:
174+
case Instruction::FPToUI:
175+
case Instruction::PtrToInt:
176+
case Instruction::IntToPtr:
177+
return R->getUnderlyingInstr()->getType();
178+
case Instruction::Freeze:
179+
case Instruction::FNeg:
180+
case Instruction::GetElementPtr:
181+
return inferScalarType(R->getOperand(0));
182+
case Instruction::Load:
183+
return cast<LoadInst>(R->getUnderlyingInstr())->getType();
184+
default:
185+
break;
186+
}
187+
// Type inference not implemented for opcode.
188+
LLVM_DEBUG({
189+
dbgs() << "LV: Found unhandled opcode for: ";
190+
R->getVPSingleValue()->dump();
191+
});
192+
llvm_unreachable("Unhandled opcode");
193+
}
194+
195+
Type *VPTypeAnalysis::inferScalarType(const VPValue *V) {
196+
if (Type *CachedTy = CachedTypes.lookup(V))
197+
return CachedTy;
198+
199+
if (V->isLiveIn())
200+
return V->getLiveInIRValue()->getType();
201+
202+
Type *ResultTy =
203+
TypeSwitch<const VPRecipeBase *, Type *>(V->getDefiningRecipe())
204+
.Case<VPCanonicalIVPHIRecipe, VPFirstOrderRecurrencePHIRecipe,
205+
VPReductionPHIRecipe, VPWidenPointerInductionRecipe>(
206+
[this](const auto *R) {
207+
// Handle header phi recipes, except VPWienIntOrFpInduction
208+
// which needs special handling due it being possibly truncated.
209+
// TODO: consider inferring/caching type of siblings, e.g.,
210+
// backedge value, here and in cases below.
211+
return inferScalarType(R->getStartValue());
212+
})
213+
.Case<VPWidenIntOrFpInductionRecipe, VPDerivedIVRecipe>(
214+
[](const auto *R) { return R->getScalarType(); })
215+
.Case<VPPredInstPHIRecipe, VPWidenPHIRecipe, VPScalarIVStepsRecipe,
216+
VPWidenGEPRecipe>([this](const VPRecipeBase *R) {
217+
return inferScalarType(R->getOperand(0));
218+
})
219+
.Case<VPBlendRecipe, VPInstruction, VPWidenRecipe, VPReplicateRecipe,
220+
VPWidenCallRecipe, VPWidenMemoryInstructionRecipe,
221+
VPWidenSelectRecipe>(
222+
[this](const auto *R) { return inferScalarTypeForRecipe(R); })
223+
.Case<VPInterleaveRecipe>([V](const VPInterleaveRecipe *R) {
224+
// TODO: Use info from interleave group.
225+
return V->getUnderlyingValue()->getType();
226+
})
227+
.Case<VPWidenCastRecipe>(
228+
[](const VPWidenCastRecipe *R) { return R->getResultType(); });
229+
assert(ResultTy && "could not infer type for the given VPValue");
230+
CachedTypes[V] = ResultTy;
231+
return ResultTy;
232+
}

0 commit comments

Comments
 (0)