-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[LV] Support binary and unary operations with EVL-vectorization #93854
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 all commits
84c1204
229b94c
1416588
e5af0d5
61c08ce
83512bb
730eb10
a2890b3
8fb08bc
9cffa8f
73a7df3
906c430
7ff8c04
7987c79
1f8726e
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 |
---|---|---|
|
@@ -923,6 +923,7 @@ class VPSingleDefRecipe : public VPRecipeBase, public VPValue { | |
case VPRecipeBase::VPWidenCastSC: | ||
case VPRecipeBase::VPWidenGEPSC: | ||
case VPRecipeBase::VPWidenSC: | ||
case VPRecipeBase::VPWidenEVLSC: | ||
case VPRecipeBase::VPWidenSelectSC: | ||
case VPRecipeBase::VPBlendSC: | ||
case VPRecipeBase::VPPredInstPHISC: | ||
|
@@ -1107,6 +1108,7 @@ class VPRecipeWithIRFlags : public VPSingleDefRecipe { | |
static inline bool classof(const VPRecipeBase *R) { | ||
return R->getVPDefID() == VPRecipeBase::VPInstructionSC || | ||
R->getVPDefID() == VPRecipeBase::VPWidenSC || | ||
R->getVPDefID() == VPRecipeBase::VPWidenEVLSC || | ||
R->getVPDefID() == VPRecipeBase::VPWidenGEPSC || | ||
R->getVPDefID() == VPRecipeBase::VPWidenCastSC || | ||
R->getVPDefID() == VPRecipeBase::VPReplicateSC || | ||
|
@@ -1410,11 +1412,16 @@ class VPInstruction : public VPRecipeWithIRFlags { | |
class VPWidenRecipe : public VPRecipeWithIRFlags { | ||
unsigned Opcode; | ||
|
||
protected: | ||
template <typename IterT> | ||
VPWidenRecipe(unsigned VPDefOpcode, Instruction &I, | ||
iterator_range<IterT> Operands) | ||
: VPRecipeWithIRFlags(VPDefOpcode, Operands, I), Opcode(I.getOpcode()) {} | ||
|
||
public: | ||
template <typename IterT> | ||
VPWidenRecipe(Instruction &I, iterator_range<IterT> Operands) | ||
: VPRecipeWithIRFlags(VPDef::VPWidenSC, Operands, I), | ||
Opcode(I.getOpcode()) {} | ||
: VPWidenRecipe(VPDef::VPWidenSC, I, Operands) {} | ||
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. This should probably have a custom 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. why ? Was the goal of having dedicated EVL-recipes to prevent treating them as non-EVL ? 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. The main benefit from having a shared base-class is so analyses don't have to handle all recipes when it makes sense. I think analyses that apply to VPWidenRecipe should also conservatively apply to WPWidenEVLRecipe, as the later only possibly operates on fewer values. If that's not sound, we probably shouldn't inherit from VPWidenLoad/VPWidenLoadEVL only share 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 assume than you really meant to introduce base class for
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. Ideally VPWidenRecipe could serve as such a base class as mentioned above, unless there’s a compelling reason not to. That way we automatically benefit from all folds and analysis already implemented for VPWidenRecipe 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. Not sure I understand added hierarchy then. If the goal is to allow reuse of existing code, then what is different in
Anyway, I'm ok to extend classof 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. Thanks for the update. Adjusting |
||
|
||
~VPWidenRecipe() override = default; | ||
|
||
|
@@ -1424,7 +1431,15 @@ class VPWidenRecipe : public VPRecipeWithIRFlags { | |
return R; | ||
} | ||
|
||
VP_CLASSOF_IMPL(VPDef::VPWidenSC) | ||
static inline bool classof(const VPRecipeBase *R) { | ||
return R->getVPDefID() == VPRecipeBase::VPWidenSC || | ||
R->getVPDefID() == VPRecipeBase::VPWidenEVLSC; | ||
} | ||
|
||
static inline bool classof(const VPUser *U) { | ||
auto *R = dyn_cast<VPRecipeBase>(U); | ||
return R && classof(R); | ||
} | ||
|
||
/// Produce a widened instruction using the opcode and operands of the recipe, | ||
/// processing State.VF elements. | ||
|
@@ -1443,6 +1458,54 @@ class VPWidenRecipe : public VPRecipeWithIRFlags { | |
#endif | ||
}; | ||
|
||
/// A recipe for widening operations with vector-predication intrinsics with | ||
/// explicit vector length (EVL). | ||
class VPWidenEVLRecipe : public VPWidenRecipe { | ||
nikolaypanchenko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
using VPRecipeWithIRFlags::transferFlags; | ||
|
||
public: | ||
template <typename IterT> | ||
VPWidenEVLRecipe(Instruction &I, iterator_range<IterT> Operands, VPValue &EVL) | ||
LiqinWeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
: VPWidenRecipe(VPDef::VPWidenEVLSC, I, Operands) { | ||
addOperand(&EVL); | ||
} | ||
VPWidenEVLRecipe(VPWidenRecipe &W, VPValue &EVL) | ||
: VPWidenEVLRecipe(*W.getUnderlyingInstr(), W.operands(), EVL) { | ||
transferFlags(W); | ||
} | ||
|
||
~VPWidenEVLRecipe() override = default; | ||
|
||
VPWidenRecipe *clone() override final { | ||
nikolaypanchenko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
llvm_unreachable("VPWidenEVLRecipe cannot be cloned"); | ||
return nullptr; | ||
} | ||
|
||
VP_CLASSOF_IMPL(VPDef::VPWidenEVLSC); | ||
|
||
VPValue *getEVL() { return getOperand(getNumOperands() - 1); } | ||
const VPValue *getEVL() const { return getOperand(getNumOperands() - 1); } | ||
|
||
/// Produce a vp-intrinsic using the opcode and operands of the recipe, | ||
/// processing EVL elements. | ||
void execute(VPTransformState &State) override final; | ||
|
||
/// Returns true if the recipe only uses the first lane of operand \p Op. | ||
bool onlyFirstLaneUsed(const VPValue *Op) const override { | ||
assert(is_contained(operands(), Op) && | ||
"Op must be an operand of the recipe"); | ||
// EVL in that recipe is always the last operand, thus any use before means | ||
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 be good to post a follow-up patch to enforce EVL only used as last operand of various recipes in the verifier 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. Sure. I will create PR with verification I added and removed previously 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. Great thanks! |
||
// the VPValue should be vectorized. | ||
return getEVL() == Op; | ||
} | ||
|
||
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | ||
/// Print the recipe. | ||
void print(raw_ostream &O, const Twine &Indent, | ||
VPSlotTracker &SlotTracker) const override final; | ||
#endif | ||
}; | ||
|
||
/// VPWidenCastRecipe is a recipe to create vector cast instructions. | ||
class VPWidenCastRecipe : public VPRecipeWithIRFlags { | ||
/// Cast instruction opcode. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ | |
#include "llvm/IR/Instructions.h" | ||
#include "llvm/IR/Type.h" | ||
#include "llvm/IR/Value.h" | ||
#include "llvm/IR/VectorBuilder.h" | ||
#include "llvm/Support/Casting.h" | ||
#include "llvm/Support/CommandLine.h" | ||
#include "llvm/Support/Debug.h" | ||
|
@@ -74,6 +75,7 @@ bool VPRecipeBase::mayWriteToMemory() const { | |
case VPWidenLoadSC: | ||
case VPWidenPHISC: | ||
case VPWidenSC: | ||
case VPWidenEVLSC: | ||
case VPWidenSelectSC: { | ||
const Instruction *I = | ||
dyn_cast_or_null<Instruction>(getVPSingleValue()->getUnderlyingValue()); | ||
|
@@ -114,6 +116,7 @@ bool VPRecipeBase::mayReadFromMemory() const { | |
case VPWidenIntOrFpInductionSC: | ||
case VPWidenPHISC: | ||
case VPWidenSC: | ||
case VPWidenEVLSC: | ||
case VPWidenSelectSC: { | ||
const Instruction *I = | ||
dyn_cast_or_null<Instruction>(getVPSingleValue()->getUnderlyingValue()); | ||
|
@@ -164,6 +167,7 @@ bool VPRecipeBase::mayHaveSideEffects() const { | |
case VPWidenPHISC: | ||
case VPWidenPointerInductionSC: | ||
case VPWidenSC: | ||
case VPWidenEVLSC: | ||
case VPWidenSelectSC: { | ||
const Instruction *I = | ||
dyn_cast_or_null<Instruction>(getVPSingleValue()->getUnderlyingValue()); | ||
|
@@ -1262,6 +1266,45 @@ InstructionCost VPWidenRecipe::computeCost(ElementCount VF, | |
} | ||
} | ||
|
||
void VPWidenEVLRecipe::execute(VPTransformState &State) { | ||
unsigned Opcode = getOpcode(); | ||
// TODO: Support other opcodes | ||
if (!Instruction::isBinaryOp(Opcode) && !Instruction::isUnaryOp(Opcode)) | ||
llvm_unreachable("Unsupported opcode in VPWidenEVLRecipe::execute"); | ||
|
||
State.setDebugLocFrom(getDebugLoc()); | ||
assert(State.UF == 1 && "Expected only UF == 1 when vectorizing with " | ||
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. Sorry, I don't know much about vectorized design. I would like to know why UF is forced to be set to 1? 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's only for EVL-vectorization, as for now it's easier not to handle multiple |
||
"explicit vector length."); | ||
VPValue *Op0 = getOperand(0); | ||
|
||
assert(State.get(Op0, 0)->getType()->isVectorTy() && | ||
"VPWidenEVLRecipe should not be used for scalars"); | ||
|
||
VPValue *EVL = getEVL(); | ||
Value *EVLArg = State.get(EVL, 0, /*NeedsScalar=*/true); | ||
IRBuilderBase &BuilderIR = State.Builder; | ||
VectorBuilder Builder(BuilderIR); | ||
Value *Mask = BuilderIR.CreateVectorSplat(State.VF, BuilderIR.getTrue()); | ||
|
||
SmallVector<Value *, 4> Ops; | ||
for (unsigned I = 0, E = getNumOperands() - 1; I < E; ++I) { | ||
VPValue *VPOp = getOperand(I); | ||
Ops.push_back(State.get(VPOp, 0)); | ||
} | ||
|
||
Builder.setMask(Mask).setEVL(EVLArg); | ||
Value *VPInst = | ||
Builder.createVectorInstruction(Opcode, Ops[0]->getType(), Ops, "vp.op"); | ||
// Currently vp-intrinsics only accept FMF flags. | ||
// TODO: Enable other flags when support is added. | ||
if (isa<FPMathOperator>(VPInst)) | ||
nikolaypanchenko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
setFlags(cast<Instruction>(VPInst)); | ||
|
||
State.set(this, VPInst, 0); | ||
State.addMetadata(VPInst, | ||
dyn_cast_or_null<Instruction>(getUnderlyingValue())); | ||
} | ||
|
||
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | ||
void VPWidenRecipe::print(raw_ostream &O, const Twine &Indent, | ||
VPSlotTracker &SlotTracker) const { | ||
|
@@ -1271,6 +1314,15 @@ void VPWidenRecipe::print(raw_ostream &O, const Twine &Indent, | |
printFlags(O); | ||
printOperands(O, SlotTracker); | ||
} | ||
|
||
void VPWidenEVLRecipe::print(raw_ostream &O, const Twine &Indent, | ||
VPSlotTracker &SlotTracker) const { | ||
O << Indent << "WIDEN-VP "; | ||
printAsOperand(O, SlotTracker); | ||
O << " = " << Instruction::getOpcodeName(getOpcode()); | ||
printFlags(O); | ||
printOperands(O, SlotTracker); | ||
} | ||
#endif | ||
|
||
void VPWidenCastRecipe::execute(VPTransformState &State) { | ||
|
Uh oh!
There was an error while loading. Please reload this page.