-
Notifications
You must be signed in to change notification settings - Fork 13.3k
[VPlan] Introduce VPInstructionWithType, use instead of VPScalarCast(NFC) #129706
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
29a14c1
060d094
3435b9f
e8623c4
55458d3
1280413
980a2f7
d9fb162
b0ddcb9
a2ec8d1
b08078a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4495,7 +4495,6 @@ static bool willGenerateVectors(VPlan &Plan, ElementCount VF, | |
switch (R.getVPDefID()) { | ||
case VPDef::VPDerivedIVSC: | ||
case VPDef::VPScalarIVStepsSC: | ||
case VPDef::VPScalarCastSC: | ||
case VPDef::VPReplicateSC: | ||
case VPDef::VPInstructionSC: | ||
case VPDef::VPCanonicalIVPHISC: | ||
|
@@ -10440,8 +10439,9 @@ preparePlanForEpilogueVectorLoop(VPlan &Plan, Loop *L, | |
assert(all_of(IV->users(), | ||
[](const VPUser *U) { | ||
return isa<VPScalarIVStepsRecipe>(U) || | ||
isa<VPScalarCastRecipe>(U) || | ||
isa<VPDerivedIVRecipe>(U) || | ||
Instruction::isCast( | ||
cast<VPInstruction>(U)->getOpcode()) || | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth wrapping in VPInstruction::isCast()? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done thanks! |
||
cast<VPInstruction>(U)->getOpcode() == | ||
Instruction::Add; | ||
}) && | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -531,7 +531,6 @@ class VPSingleDefRecipe : public VPRecipeBase, public VPValue { | |||||
case VPRecipeBase::VPWidenIntOrFpInductionSC: | ||||||
case VPRecipeBase::VPWidenPointerInductionSC: | ||||||
case VPRecipeBase::VPReductionPHISC: | ||||||
case VPRecipeBase::VPScalarCastSC: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. VPInstructionSC above now also holds for VPInstructionWithType recipes, instead of VPScalarCastSC. |
||||||
case VPRecipeBase::VPScalarPHISC: | ||||||
case VPRecipeBase::VPPartialReductionSC: | ||||||
return true; | ||||||
|
@@ -1025,6 +1024,55 @@ class VPInstruction : public VPRecipeWithIRFlags, | |||||
StringRef getName() const { return Name; } | ||||||
}; | ||||||
|
||||||
/// A specialization of VPInstruction augmenting it with a dedicated result | ||||||
/// type, to be used when the opcode and operands of the VPInstruction don't | ||||||
/// directly determine the result type. | ||||||
class VPInstructionWithType : public VPInstruction { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A recipe can "have" a single def, and support the interface of VPValue, by inheriting from it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would the Type base class store the Type or just provide an interface for e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should split off the flags from VPRecipeWithFlags and generally move in this direction. For the type, this would just add a type field and a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice if we could do something like d6eb747, but unfortunately I've not yet been able to figure out how to make this not actually crash. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I managed to sort this out now using a specialization of A sketch is here 5c54367, might be worth doing separately> |
||||||
/// Scalar result type produced by the recipe. | ||||||
Type *ResultTy; | ||||||
|
||||||
Value *generate(VPTransformState &State); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Document. May be a bit confusing being non-virtual nor override, worth a comment? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I folded it into |
||||||
|
||||||
public: | ||||||
VPInstructionWithType(unsigned Opcode, ArrayRef<VPValue *> Operands, | ||||||
Type *ResultTy, DebugLoc DL, const Twine &Name = "") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth documenting somewhere that VPInstructionWithType does not have its own VPDef::VPInstructionWithTypeSC identifying it, as do all other recipes, but it uses VPInstructionSC instead. VPInstructions should be identified according to their opcode, whether they are actually of class VPInstruction itself or a subclass thereof. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done thanks |
||||||
: VPInstruction(Opcode, Operands, DL, Name), ResultTy(ResultTy) {} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about FMF for, say, FPExt, or any other IRFlags supported by VPInstruction casts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For now, non of the cast take/use them, can be added as follow-up? |
||||||
|
||||||
static inline bool classof(const VPRecipeBase *R) { | ||||||
auto *VPI = dyn_cast<VPInstruction>(R); | ||||||
return VPI && Instruction::isCast(VPI->getOpcode()); | ||||||
} | ||||||
|
||||||
static inline bool classof(const VPUser *R) { | ||||||
return isa<VPInstructionWithType>(cast<VPRecipeBase>(R)); | ||||||
} | ||||||
|
||||||
VPInstruction *clone() override { | ||||||
auto *New = | ||||||
new VPInstructionWithType(getOpcode(), {getOperand(0)}, getResultType(), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Assumed to have a single operand? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For now yes, but removed restriction, thanks! |
||||||
getDebugLoc(), getName()); | ||||||
New->setUnderlyingValue(getUnderlyingValue()); | ||||||
return New; | ||||||
} | ||||||
|
||||||
void execute(VPTransformState &State) override; | ||||||
|
||||||
/// Return the cost of this VPIRInstruction. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed thanks |
||||||
InstructionCost computeCost(ElementCount VF, | ||||||
VPCostContext &Ctx) const override { | ||||||
// TODO: Compute accurate cost after retiring the legacy cost model. | ||||||
return 0; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return actual cost here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For now this only represents scalar casts, which are not corresponding to any input IR, hence are not yet costed (Added a TODO). It will be fleshed out in #129712. |
||||||
} | ||||||
|
||||||
Type *getResultType() const { return ResultTy; } | ||||||
|
||||||
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | ||||||
/// Print the recipe. | ||||||
void print(raw_ostream &O, const Twine &Indent, | ||||||
VPSlotTracker &SlotTracker) const override; | ||||||
#endif | ||||||
}; | ||||||
|
||||||
/// A recipe to wrap on original IR instruction not to be modified during | ||||||
/// execution, execept for PHIs. For PHIs, a single VPValue operand is allowed, | ||||||
/// and it is used to add a new incoming value for the single predecessor VPBB. | ||||||
|
@@ -1182,54 +1230,6 @@ class VPWidenCastRecipe : public VPRecipeWithIRFlags { | |||||
Type *getResultType() const { return ResultTy; } | ||||||
}; | ||||||
|
||||||
/// VPScalarCastRecipe is a recipe to create scalar cast instructions. | ||||||
class VPScalarCastRecipe : public VPSingleDefRecipe { | ||||||
Instruction::CastOps Opcode; | ||||||
|
||||||
Type *ResultTy; | ||||||
|
||||||
Value *generate(VPTransformState &State); | ||||||
|
||||||
public: | ||||||
VPScalarCastRecipe(Instruction::CastOps Opcode, VPValue *Op, Type *ResultTy, | ||||||
DebugLoc DL) | ||||||
: VPSingleDefRecipe(VPDef::VPScalarCastSC, {Op}, DL), Opcode(Opcode), | ||||||
ResultTy(ResultTy) {} | ||||||
|
||||||
~VPScalarCastRecipe() override = default; | ||||||
|
||||||
VPScalarCastRecipe *clone() override { | ||||||
return new VPScalarCastRecipe(Opcode, getOperand(0), ResultTy, | ||||||
getDebugLoc()); | ||||||
} | ||||||
|
||||||
VP_CLASSOF_IMPL(VPDef::VPScalarCastSC) | ||||||
|
||||||
void execute(VPTransformState &State) override; | ||||||
|
||||||
/// Return the cost of this VPScalarCastRecipe. | ||||||
InstructionCost computeCost(ElementCount VF, | ||||||
VPCostContext &Ctx) const override { | ||||||
// TODO: Compute accurate cost after retiring the legacy cost model. | ||||||
return 0; | ||||||
} | ||||||
|
||||||
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | ||||||
void print(raw_ostream &O, const Twine &Indent, | ||||||
VPSlotTracker &SlotTracker) const override; | ||||||
#endif | ||||||
|
||||||
/// Returns the result type of the cast. | ||||||
Type *getResultType() const { return ResultTy; } | ||||||
|
||||||
bool onlyFirstLaneUsed(const VPValue *Op) const override { | ||||||
// At the moment, only uniform codegen is implemented. | ||||||
assert(is_contained(operands(), Op) && | ||||||
"Op must be an operand of the recipe"); | ||||||
return true; | ||||||
} | ||||||
}; | ||||||
|
||||||
/// A recipe for widening vector intrinsics. | ||||||
class VPWidenIntrinsicRecipe : public VPRecipeWithIRFlags { | ||||||
/// ID of the vector intrinsic to widen. | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -259,20 +259,17 @@ Type *VPTypeAnalysis::inferScalarType(const VPValue *V) { | |
VPPartialReductionRecipe>([this](const VPRecipeBase *R) { | ||
return inferScalarType(R->getOperand(0)); | ||
}) | ||
.Case<VPInstructionWithType, VPWidenIntrinsicRecipe>( | ||
[](const auto *R) { return R->getResultType(); }) | ||
Comment on lines
+265
to
+266
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth noting that this case should appear before the next to catch VPInstructionWithType before its parent VPInstruction? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, thanks! |
||
.Case<VPBlendRecipe, VPInstruction, VPWidenRecipe, VPReplicateRecipe, | ||
VPWidenCallRecipe, VPWidenMemoryRecipe, VPWidenSelectRecipe>( | ||
[this](const auto *R) { return inferScalarTypeForRecipe(R); }) | ||
.Case<VPWidenIntrinsicRecipe>([](const VPWidenIntrinsicRecipe *R) { | ||
return R->getResultType(); | ||
}) | ||
.Case<VPInterleaveRecipe>([V](const VPInterleaveRecipe *R) { | ||
// TODO: Use info from interleave group. | ||
return V->getUnderlyingValue()->getType(); | ||
}) | ||
.Case<VPWidenCastRecipe>( | ||
[](const VPWidenCastRecipe *R) { return R->getResultType(); }) | ||
.Case<VPScalarCastRecipe>( | ||
[](const VPScalarCastRecipe *R) { return R->getResultType(); }) | ||
.Case<VPExpandSCEVRecipe>([](const VPExpandSCEVRecipe *R) { | ||
return R->getSCEV()->getType(); | ||
}) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -147,7 +147,6 @@ bool VPRecipeBase::mayHaveSideEffects() const { | |
switch (getVPDefID()) { | ||
case VPDerivedIVSC: | ||
case VPPredInstPHISC: | ||
case VPScalarCastSC: | ||
lukel97 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
case VPVectorEndPointerSC: | ||
return false; | ||
case VPInstructionSC: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Which now takes care of VPInstructionWithType recipes as well, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep |
||
|
@@ -418,7 +417,7 @@ bool VPInstruction::doesGeneratePerAllLanes() const { | |
} | ||
|
||
bool VPInstruction::canGenerateScalarForFirstLane() const { | ||
if (Instruction::isBinaryOp(getOpcode())) | ||
if (Instruction::isBinaryOp(getOpcode()) || Instruction::isCast(getOpcode())) | ||
return true; | ||
if (isSingleScalar() || isVectorToScalar()) | ||
return true; | ||
|
@@ -870,7 +869,7 @@ void VPInstruction::execute(VPTransformState &State) { | |
} | ||
|
||
bool VPInstruction::opcodeMayReadOrWriteFromMemory() const { | ||
if (Instruction::isBinaryOp(getOpcode())) | ||
if (Instruction::isBinaryOp(getOpcode()) || Instruction::isCast(getOpcode())) | ||
return false; | ||
switch (getOpcode()) { | ||
case Instruction::ExtractElement: | ||
|
@@ -893,7 +892,7 @@ bool VPInstruction::opcodeMayReadOrWriteFromMemory() const { | |
|
||
bool VPInstruction::onlyFirstLaneUsed(const VPValue *Op) const { | ||
assert(is_contained(operands(), Op) && "Op must be an operand of the recipe"); | ||
if (Instruction::isBinaryOp(getOpcode())) | ||
if (Instruction::isBinaryOp(getOpcode()) || Instruction::isCast(getOpcode())) | ||
return vputils::onlyFirstLaneUsed(this); | ||
|
||
switch (getOpcode()) { | ||
|
@@ -1025,6 +1024,39 @@ void VPInstruction::print(raw_ostream &O, const Twine &Indent, | |
} | ||
#endif | ||
|
||
Value *VPInstructionWithType::generate(VPTransformState &State) { | ||
State.setDebugLocFrom(getDebugLoc()); | ||
assert(vputils::onlyFirstLaneUsed(this) && | ||
"Codegen only implemented for first lane."); | ||
switch (getOpcode()) { | ||
case Instruction::SExt: | ||
case Instruction::ZExt: | ||
case Instruction::Trunc: { | ||
// Note: SExt/ZExt not used yet. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then leave them as default until they are, to avoid dead code? Admittedly unrelated. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Limited to Trunc and ZExt, which both are used, thanks |
||
Value *Op = State.get(getOperand(0), VPLane(0)); | ||
return State.Builder.CreateCast(Instruction::CastOps(getOpcode()), Op, | ||
ResultTy); | ||
} | ||
default: | ||
llvm_unreachable("opcode not implemented yet"); | ||
} | ||
} | ||
|
||
void VPInstructionWithType::execute(VPTransformState &State) { | ||
State.set(this, generate(State), VPLane(0)); | ||
} | ||
|
||
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | ||
void VPInstructionWithType::print(raw_ostream &O, const Twine &Indent, | ||
VPSlotTracker &SlotTracker) const { | ||
O << Indent << "EMIT "; | ||
printAsOperand(O, SlotTracker); | ||
O << " = " << Instruction::getOpcodeName(getOpcode()) << " "; | ||
printOperands(O, SlotTracker); | ||
O << " to " << *ResultTy; | ||
} | ||
#endif | ||
|
||
void VPIRInstruction::execute(VPTransformState &State) { | ||
assert((isa<PHINode>(&I) || getNumOperands() == 0) && | ||
"Only PHINodes can have extra operands"); | ||
|
@@ -2497,38 +2529,6 @@ void VPReplicateRecipe::print(raw_ostream &O, const Twine &Indent, | |
} | ||
#endif | ||
|
||
Value *VPScalarCastRecipe ::generate(VPTransformState &State) { | ||
State.setDebugLocFrom(getDebugLoc()); | ||
assert(vputils::onlyFirstLaneUsed(this) && | ||
"Codegen only implemented for first lane."); | ||
switch (Opcode) { | ||
case Instruction::SExt: | ||
case Instruction::ZExt: | ||
case Instruction::Trunc: { | ||
// Note: SExt/ZExt not used yet. | ||
Value *Op = State.get(getOperand(0), VPLane(0)); | ||
return State.Builder.CreateCast(Instruction::CastOps(Opcode), Op, ResultTy); | ||
} | ||
default: | ||
llvm_unreachable("opcode not implemented yet"); | ||
} | ||
} | ||
|
||
void VPScalarCastRecipe ::execute(VPTransformState &State) { | ||
State.set(this, generate(State), VPLane(0)); | ||
} | ||
|
||
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | ||
void VPScalarCastRecipe ::print(raw_ostream &O, const Twine &Indent, | ||
VPSlotTracker &SlotTracker) const { | ||
O << Indent << "SCALAR-CAST "; | ||
printAsOperand(O, SlotTracker); | ||
O << " = " << Instruction::getOpcodeName(Opcode) << " "; | ||
printOperands(O, SlotTracker); | ||
O << " to " << *ResultTy; | ||
} | ||
#endif | ||
|
||
void VPBranchOnMaskRecipe::execute(VPTransformState &State) { | ||
State.setDebugLocFrom(getDebugLoc()); | ||
assert(State.Lane && "Branch on Mask works only on single instance."); | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -147,8 +147,8 @@ bool VPlanVerifier::verifyEVLRecipe(const VPInstruction &EVL) const { | |||||
[&](const VPRecipeBase *S) { return VerifyEVLUse(*S, 2); }) | ||||||
.Case<VPWidenLoadEVLRecipe, VPVectorEndPointerRecipe>( | ||||||
[&](const VPRecipeBase *R) { return VerifyEVLUse(*R, 1); }) | ||||||
.Case<VPScalarCastRecipe>( | ||||||
[&](const VPScalarCastRecipe *S) { return VerifyEVLUse(*S, 0); }) | ||||||
.Case<VPInstructionWithType>( | ||||||
[&](const auto *S) { return VerifyEVLUse(*S, 0); }) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
consistency? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. changed to |
||||||
.Case<VPInstruction>([&](const VPInstruction *I) { | ||||||
if (I->getOpcode() == Instruction::PHI) | ||||||
return VerifyEVLUse(*I, 1); | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
VPInstructionSC now also holds for VPInstructionWithType recipes, instead of VPScalarCastSC.