1//===-- X86PartialReduction.cpp -------------------------------------------===// 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 7//===----------------------------------------------------------------------===// 9// This pass looks for add instructions used by a horizontal reduction to see 10// if we might be able to use pmaddwd or psadbw. Some cases of this require 11// cross basic block knowledge and can't be done in SelectionDAG. 13//===----------------------------------------------------------------------===// 22#include "llvm/IR/IntrinsicsX86.h" 29#define DEBUG_TYPE "x86-partial-reduction" 38staticcharID;
// Pass identification, replacement for typeid. 49return"X86 Partial Reduction";
59returnnew X86PartialReduction();
62char X86PartialReduction::ID = 0;
65"X86 Partial Reduction",
false,
false)
67// This function should be aligned with detectExtMul() in X86ISelLowering.cpp. 70if (!ST->hasVNNI() && !ST->hasAVXVNNI())
76if (isa<SExtInst>(
LHS))
80if (
auto *Cast = dyn_cast<CastInst>(
Op)) {
82 (Cast->getOpcode() == Instruction::SExt ||
83 Cast->getOpcode() == Instruction::ZExt) &&
84 Cast->getOperand(0)->getType()->getScalarSizeInBits() <= 8)
88return isa<Constant>(
Op);
91// (dpbusd (zext a), (sext, b)). Since the first operand should be unsigned 92// value, we need to check LHS is zero extended value. RHS should be signed 93// value, so we just check the signed bits. 107// Need at least 8 elements. 108if (cast<FixedVectorType>(
Op->getType())->getNumElements() < 8)
111// Element type should be i32. 112if (!cast<VectorType>(
Op->getType())->getElementType()->isIntegerTy(32))
115auto *
Mul = dyn_cast<BinaryOperator>(
Op);
122// If the target support VNNI, leave it to ISel to combine reduce operation 123// to VNNI instruction. 124// TODO: we can support transforming reduce to VNNI intrinsic for across block 126if (ReduceInOneBB && matchVPDPBUSDPattern(
ST,
Mul,
DL))
129// LHS and RHS should be only used once or if they are the same then only 130// used twice. Only check this when SSE4.1 is enabled and we have zext/sext 131// instructions, otherwise we use punpck to emulate zero extend in stages. The 132// trunc/ we need to do likely won't introduce new instructions in that case. 145auto CanShrinkOp = [&](
Value *
Op) {
147if (
auto *Cast = dyn_cast<CastInst>(
Op)) {
149 (Cast->getOpcode() == Instruction::SExt ||
150 Cast->getOpcode() == Instruction::ZExt) &&
151 Cast->getOperand(0)->getType()->getScalarSizeInBits() <= 16)
155return isa<Constant>(
Op);
158// If the operation can be freely truncated and has enough sign bits we 164// SelectionDAG has limited support for truncating through an add or sub if 165// the inputs are freely truncatable. 166if (
auto *BO = dyn_cast<BinaryOperator>(
Op)) {
177// Both Ops need to be shrinkable. 178if (!CanShrinkOp(
LHS) && !CanShrinkOp(
RHS))
183auto *MulTy = cast<FixedVectorType>(
Op->getType());
184unsigned NumElts = MulTy->getNumElements();
186// Extract even elements and odd elements and add them together. This will 187// be pattern matched by SelectionDAG to pmaddwd. This instruction will be 188// half the original width. 191for (
int i = 0, e = NumElts / 2; i !=
e; ++i) {
193 OddMask[i] = i * 2 + 1;
195// Creating a new mul so the replaceAllUsesWith below doesn't replace the 196// uses in the shuffles we're creating. 198Value *EvenElts = Builder.CreateShuffleVector(NewMul, NewMul, EvenMask);
199Value *OddElts = Builder.CreateShuffleVector(NewMul, NewMul, OddMask);
200Value *
MAdd = Builder.CreateAdd(EvenElts, OddElts);
202// Concatenate zeroes to extend back to the original type. 204 std::iota(ConcatMask.begin(), ConcatMask.end(), 0);
206Value *
Concat = Builder.CreateShuffleVector(MAdd, Zero, ConcatMask);
214bool X86PartialReduction::trySADReplacement(
Instruction *
Op) {
218// TODO: There's nothing special about i32, any integer type above i16 should 220if (!cast<VectorType>(
Op->getType())->getElementType()->isIntegerTy(32))
224if (
match(
Op, PatternMatch::m_Intrinsic<Intrinsic::abs>())) {
225LHS =
Op->getOperand(0);
227// Operand should be a select. 228auto *
SI = dyn_cast<SelectInst>(
Op);
233// Select needs to implement absolute value. 239// Need a subtract of two values. 240auto *Sub = dyn_cast<BinaryOperator>(
LHS);
241if (!Sub || Sub->getOpcode() != Instruction::Sub)
244// Look for zero extend from i8. 246if (
auto *ZExt = dyn_cast<ZExtInst>(
Op))
247if (cast<VectorType>(ZExt->getOperand(0)->getType())
250return ZExt->getOperand(0);
255// Both operands of the subtract should be extends from vXi8. 256Value *Op0 = getZeroExtendedVal(Sub->getOperand(0));
257Value *Op1 = getZeroExtendedVal(Sub->getOperand(1));
263auto *OpTy = cast<FixedVectorType>(
Op->getType());
264unsigned NumElts = OpTy->getNumElements();
266unsigned IntrinsicNumElts;
268if (
ST->hasBWI() && NumElts >= 64) {
269 IID = Intrinsic::x86_avx512_psad_bw_512;
270 IntrinsicNumElts = 64;
271 }
elseif (
ST->hasAVX2() && NumElts >= 32) {
272 IID = Intrinsic::x86_avx2_psad_bw;
273 IntrinsicNumElts = 32;
275 IID = Intrinsic::x86_sse2_psad_bw;
276 IntrinsicNumElts = 16;
282// Pad input with zeroes. 284for (
unsigned i = 0; i != NumElts; ++i)
286for (
unsigned i = NumElts; i != 16; ++i)
287 ConcatMask[i] = (i % NumElts) + NumElts;
290 Op0 = Builder.CreateShuffleVector(Op0, Zero, ConcatMask);
291 Op1 = Builder.CreateShuffleVector(Op1, Zero, ConcatMask);
295// Intrinsics produce vXi64 and need to be casted to vXi32. 299assert(NumElts % IntrinsicNumElts == 0 &&
"Unexpected number of elements!");
300unsigned NumSplits = NumElts / IntrinsicNumElts;
302// First collect the pieces we need. 304for (
unsigned i = 0; i != NumSplits; ++i) {
306 std::iota(ExtractMask.begin(), ExtractMask.end(), i * IntrinsicNumElts);
307Value *ExtractOp0 = Builder.CreateShuffleVector(Op0, Op0, ExtractMask);
308Value *ExtractOp1 = Builder.CreateShuffleVector(Op1, Op0, ExtractMask);
309 Ops[i] = Builder.CreateCall(PSADBWFn, {ExtractOp0, ExtractOp1});
310 Ops[i] = Builder.CreateBitCast(Ops[i], I32Ty);
314unsigned Stages =
Log2_32(NumSplits);
315for (
unsigned s = Stages; s > 0; --s) {
316unsigned NumConcatElts =
317 cast<FixedVectorType>(Ops[0]->
getType())->getNumElements() * 2;
318for (
unsigned i = 0; i != 1U << (s - 1); ++i) {
320 std::iota(ConcatMask.begin(), ConcatMask.end(), 0);
321 Ops[i] = Builder.CreateShuffleVector(Ops[i*2], Ops[i*2+1], ConcatMask);
325// At this point the final value should be in Ops[0]. Now we need to adjust 326// it to the final original type. 327 NumElts = cast<FixedVectorType>(OpTy)->getNumElements();
329// Extract down to 2 elements. 330 Ops[0] = Builder.CreateShuffleVector(Ops[0], Ops[0],
ArrayRef<int>{0, 1});
331 }
elseif (NumElts >= 8) {
334 cast<FixedVectorType>(Ops[0]->
getType())->getNumElements();
335for (
unsigned i = 0; i != SubElts; ++i)
337for (
unsigned i = SubElts; i != NumElts; ++i)
338 ConcatMask[i] = (i % SubElts) + SubElts;
341 Ops[0] = Builder.CreateShuffleVector(Ops[0], Zero, ConcatMask);
344Op->replaceAllUsesWith(Ops[0]);
345Op->eraseFromParent();
350// Walk backwards from the ExtractElementInst and determine if it is the end of 351// a horizontal reduction. Return the input to the reduction if we find one. 353bool &ReduceInOneBB) {
355// Make sure we're extracting index 0. 357if (!Index || !Index->isNullValue())
361if (!BO || BO->getOpcode() != Instruction::Add || !BO->hasOneUse())
364 ReduceInOneBB =
false;
366unsigned NumElems = cast<FixedVectorType>(BO->getType())->getNumElements();
367// Ensure the reduction size is a power of 2. 372unsigned Stages =
Log2_32(NumElems);
373for (
unsigned i = 0; i != Stages; ++i) {
374constauto *BO = dyn_cast<BinaryOperator>(
Op);
375if (!BO || BO->getOpcode() != Instruction::Add)
378 ReduceInOneBB =
false;
380// If this isn't the first add, then it should only have 2 users, the 381// shuffle and another add which we checked in the previous iteration. 382if (i != 0 && !BO->hasNUses(2))
388auto *Shuffle = dyn_cast<ShuffleVectorInst>(
LHS);
392 Shuffle = dyn_cast<ShuffleVectorInst>(
RHS);
396// The first operand of the shuffle should be the same as the other operand 398if (!Shuffle || Shuffle->getOperand(0) !=
Op)
401// Verify the shuffle has the expected (at this stage of the pyramid) mask. 402unsigned MaskEnd = 1 << i;
403for (
unsigned Index = 0; Index < MaskEnd; ++Index)
404if (Shuffle->getMaskValue(Index) != (
int)(MaskEnd + Index))
411// See if this BO is reachable from this Phi by walking forward through single 412// use BinaryOperators with the same opcode. If we get back then we know we've 413// found a loop and it is safe to step through this Add to find more leaves. 415// The PHI itself should only have one use. 416if (!Phi->hasOneUse())
419Instruction *U = cast<Instruction>(*Phi->user_begin());
423while (U->hasOneUse() && U->getOpcode() == BO->
getOpcode())
424 U = cast<Instruction>(*U->user_begin());
429// Collect all the leaves of the tree of adds that feeds into the horizontal 430// reduction. Root is the Value that is used by the horizontal reduction. 431// We look through single use phis, single use adds, or adds that are used by 432// a phi that forms a loop with the add. 438while (!Worklist.
empty()) {
440if (!Visited.
insert(V).second)
443if (
auto *PN = dyn_cast<PHINode>(V)) {
444// PHI node should have single use unless it is the root node, then it 446if (!PN->hasNUses(PN == Root ? 2 : 1))
449// Push incoming values to the worklist. 455if (
auto *BO = dyn_cast<BinaryOperator>(V)) {
456if (BO->getOpcode() == Instruction::Add) {
457// Simple case. Single use, just push its operands to the worklist. 458if (BO->hasNUses(BO == Root ? 2 : 1)) {
463// If there is additional use, make sure it is an unvisited phi that 464// gets us back to this node. 465if (BO->hasNUses(BO == Root ? 3 : 2)) {
467for (
auto *U : BO->users())
468if (
auto *
P = dyn_cast<PHINode>(U))
472// If we didn't find a 2-input PHI then this isn't a case we can 477// Walk forward from this phi to see if it reaches back to this add. 481// The phi forms a loop with this Add, push its operands. 487// Not an add or phi, make it a leaf. 488if (
auto *
I = dyn_cast<Instruction>(V)) {
489if (!V->hasNUses(
I == Root ? 2 : 1))
492// Add this as a leaf. 498bool X86PartialReduction::runOnFunction(
Function &
F) {
502auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
507ST =
TM.getSubtargetImpl(
F);
509DL = &
F.getDataLayout();
511bool MadeChange =
false;
514auto *EE = dyn_cast<ExtractElementInst>(&
I);
519// First find a reduction tree. 520// FIXME: Do we need to handle other opcodes than Add? 529if (tryMAddReplacement(
I, ReduceInOneBB)) {
534// Don't do SAD matching on the root node. SelectionDAG already 535// has support for that and currently generates better code. 536if (
I != Root && trySADReplacement(
I))
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
This file contains the declarations for the subclasses of Constant, which represent the different fla...
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
static SymbolRef::Type getType(const Symbol *Sym)
Target-Independent Code Generator Pass Configuration Options pass.
static constexpr int Concat[]
static bool isReachableFromPHI(PHINode *Phi, BinaryOperator *BO)
BinaryOperator const DataLayout * DL
if(isa< SExtInst >(LHS)) std auto IsFreeTruncation
static Value * matchAddReduction(const ExtractElementInst &EE, bool &ReduceInOneBB)
static void collectLeaves(Value *Root, SmallVectorImpl< Instruction * > &Leaves)
Represent the analysis usage information of a pass.
void setPreservesCFG()
This function should be called by the pass, iff they do not:
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
BinaryOps getOpcode() const
static Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
This class represents an Operation in the Expression.
A parsed version of the target data layout string in and methods for querying it.
This instruction extracts a single (scalar) element from a VectorType value.
Value * getVectorOperand()
Value * getIndexOperand()
static FixedVectorType * get(Type *ElementType, unsigned NumElts)
FunctionPass class - This class is used to implement most global optimizations.
virtual bool runOnFunction(Function &F)=0
runOnFunction - Virtual method overriden by subclasses to do the per-function processing of the pass.
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
unsigned getNumIncomingValues() const
Return the number of incoming edges.
virtual void getAnalysisUsage(AnalysisUsage &) const
getAnalysisUsage - This function should be overriden by passes that need analysis information to do t...
virtual StringRef getPassName() const
getPassName - Return a nice clean name for a pass.
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
StringRef - Represent a constant reference to a string, i.e.
Value * getOperand(unsigned i) const
LLVM Value Representation.
Type * getType() const
All values are typed, get the type of this value.
bool hasOneUse() const
Return true if there is exactly one use of this value.
void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
bool hasNUses(unsigned N) const
Return true if this Value has exactly N uses.
const ParentTy * getParent() const
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > Tys={})
Look up the Function declaration of the intrinsic id in the Module M.
bool match(Val *V, const Pattern &P)
This is an optimization pass for GlobalISel generic memory operations.
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
unsigned Log2_32(uint32_t Value)
Return the floor log base 2 of the specified value, -1 if the value is zero.
@ SPF_ABS
Floating point maxnum.
constexpr bool isPowerOf2_32(uint32_t Value)
Return true if the argument is a power of two > 0.
SelectPatternResult matchSelectPattern(Value *V, Value *&LHS, Value *&RHS, Instruction::CastOps *CastOp=nullptr, unsigned Depth=0)
Pattern match integer [SU]MIN, [SU]MAX and ABS idioms, returning the kind and providing the out param...
FunctionPass * createX86PartialReductionPass()
This pass optimizes arithmetic based on knowledge that is only used by a reduction sequence and is th...
void computeKnownBits(const Value *V, KnownBits &Known, const DataLayout &DL, unsigned Depth=0, AssumptionCache *AC=nullptr, const Instruction *CxtI=nullptr, const DominatorTree *DT=nullptr, bool UseInstrInfo=true)
Determine which bits of V are known to be either zero or one and return them in the KnownZero/KnownOn...
DWARFExpression::Operation Op
unsigned ComputeNumSignBits(const Value *Op, const DataLayout &DL, unsigned Depth=0, AssumptionCache *AC=nullptr, const Instruction *CxtI=nullptr, const DominatorTree *DT=nullptr, bool UseInstrInfo=true)
Return the number of times the sign bit of the register is replicated into the other bits.
unsigned ComputeMaxSignificantBits(const Value *Op, const DataLayout &DL, unsigned Depth=0, AssumptionCache *AC=nullptr, const Instruction *CxtI=nullptr, const DominatorTree *DT=nullptr)
Get the upper bound on bit size for this Value Op as a signed integer.
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.