blob: 9b1f7117fa5739c0cfe604d98d511c33fb4eb548 [file] [log] [blame]
//===- ComplexDeinterleavingPass.cpp --------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Identification:
// This step is responsible for finding the patterns that can be lowered to
// complex instructions, and building a graph to represent the complex
// structures. Starting from the "Converging Shuffle" (a shuffle that
// reinterleaves the complex components, with a mask of <0, 2, 1, 3>), the
// operands are evaluated and identified as "Composite Nodes" (collections of
// instructions that can potentially be lowered to a single complex
// instruction). This is performed by checking the real and imaginary components
// and tracking the data flow for each component while following the operand
// pairs. Validity of each node is expected to be done upon creation, and any
// validation errors should halt traversal and prevent further graph
// construction.
//
// Replacement:
// This step traverses the graph built up by identification, delegating to the
// target to validate and generate the correct intrinsics, and plumbs them
// together connecting each end of the new intrinsics graph to the existing
// use-def chain. This step is assumed to finish successfully, as all
// information is expected to be correct by this point.
//
//
// Internal data structure:
// ComplexDeinterleavingGraph:
// Keeps references to all the valid CompositeNodes formed as part of the
// transformation, and every Instruction contained within said nodes. It also
// holds onto a reference to the root Instruction, and the root node that should
// replace it.
//
// ComplexDeinterleavingCompositeNode:
// A CompositeNode represents a single transformation point; each node should
// transform into a single complex instruction (ignoring vector splitting, which
// would generate more instructions per node). They are identified in a
// depth-first manner, traversing and identifying the operands of each
// instruction in the order they appear in the IR.
// Each node maintains a reference to its Real and Imaginary instructions,
// as well as any additional instructions that make up the identified operation
// (Internal instructions should only have uses within their containing node).
// A Node also contains the rotation and operation type that it represents.
// Operands contains pointers to other CompositeNodes, acting as the edges in
// the graph. ReplacementValue is the transformed Value* that has been emitted
// to the IR.
//
// Note: If the operation of a Node is Shuffle, only the Real, Imaginary, and
// ReplacementValue fields of that Node are relevant, where the ReplacementValue
// should be pre-populated.
//
//===----------------------------------------------------------------------===//
#include "llvm/CodeGen/ComplexDeinterleavingPass.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/InitializePasses.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/Utils/Local.h"
#include <algorithm>
using namespace llvm;
using namespace PatternMatch;
#define DEBUG_TYPE "complex-deinterleaving"
STATISTIC(NumComplexTransformations, "Amount of complex patterns transformed");
static cl::opt<bool> ComplexDeinterleavingEnabled(
"enable-complex-deinterleaving",
cl::desc("Enable generation of complex instructions"), cl::init(true),
cl::Hidden);
/// Checks the given mask, and determines whether said mask is interleaving.
///
/// To be interleaving, a mask must alternate between `i` and `i + (Length /
/// 2)`, and must contain all numbers within the range of `[0..Length)` (e.g. a
/// 4x vector interleaving mask would be <0, 2, 1, 3>).
static bool isInterleavingMask(ArrayRef<int> Mask);
/// Checks the given mask, and determines whether said mask is deinterleaving.
///
/// To be deinterleaving, a mask must increment in steps of 2, and either start
/// with 0 or 1.
/// (e.g. an 8x vector deinterleaving mask would be either <0, 2, 4, 6> or
/// <1, 3, 5, 7>).
static bool isDeinterleavingMask(ArrayRef<int> Mask);
namespace {
class ComplexDeinterleavingLegacyPass : public FunctionPass {
public:
static char ID;
ComplexDeinterleavingLegacyPass(const TargetMachine *TM = nullptr)
: FunctionPass(ID), TM(TM) {
initializeComplexDeinterleavingLegacyPassPass(
*PassRegistry::getPassRegistry());
}
StringRef getPassName() const override {
return "Complex Deinterleaving Pass";
}
bool runOnFunction(Function &F) override;
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<TargetLibraryInfoWrapperPass>();
AU.setPreservesCFG();
}
private:
const TargetMachine *TM;
};
class ComplexDeinterleavingGraph;
struct ComplexDeinterleavingCompositeNode {
ComplexDeinterleavingCompositeNode(ComplexDeinterleavingOperation Op,
Instruction *R, Instruction *I)
: Operation(Op), Real(R), Imag(I) {}
private:
friend class ComplexDeinterleavingGraph;
using NodePtr = std::shared_ptr<ComplexDeinterleavingCompositeNode>;
using RawNodePtr = ComplexDeinterleavingCompositeNode *;
public:
ComplexDeinterleavingOperation Operation;
Instruction *Real;
Instruction *Imag;
// Instructions that should only exist within this node, there should be no
// users of these instructions outside the node. An example of these would be
// the multiply instructions of a partial multiply operation.
SmallVector<Instruction *> InternalInstructions;
ComplexDeinterleavingRotation Rotation;
SmallVector<RawNodePtr> Operands;
Value *ReplacementNode = nullptr;
void addInstruction(Instruction *I) { InternalInstructions.push_back(I); }
void addOperand(NodePtr Node) { Operands.push_back(Node.get()); }
bool hasAllInternalUses(SmallPtrSet<Instruction *, 16> &AllInstructions);
void dump() { dump(dbgs()); }
void dump(raw_ostream &OS) {
auto PrintValue = [&](Value *V) {
if (V) {
OS << "\"";
V->print(OS, true);
OS << "\"\n";
} else
OS << "nullptr\n";
};
auto PrintNodeRef = [&](RawNodePtr Ptr) {
if (Ptr)
OS << Ptr << "\n";
else
OS << "nullptr\n";
};
OS << "- CompositeNode: " << this << "\n";
OS << " Real: ";
PrintValue(Real);
OS << " Imag: ";
PrintValue(Imag);
OS << " ReplacementNode: ";
PrintValue(ReplacementNode);
OS << " Operation: " << (int)Operation << "\n";
OS << " Rotation: " << ((int)Rotation * 90) << "\n";
OS << " Operands: \n";
for (const auto &Op : Operands) {
OS << " - ";
PrintNodeRef(Op);
}
OS << " InternalInstructions:\n";
for (const auto &I : InternalInstructions) {
OS << " - \"";
I->print(OS, true);
OS << "\"\n";
}
}
};
class ComplexDeinterleavingGraph {
public:
using NodePtr = ComplexDeinterleavingCompositeNode::NodePtr;
using RawNodePtr = ComplexDeinterleavingCompositeNode::RawNodePtr;
explicit ComplexDeinterleavingGraph(const TargetLowering *tl) : TL(tl) {}
private:
const TargetLowering *TL;
Instruction *RootValue;
NodePtr RootNode;
SmallVector<NodePtr> CompositeNodes;
SmallPtrSet<Instruction *, 16> AllInstructions;
NodePtr prepareCompositeNode(ComplexDeinterleavingOperation Operation,
Instruction *R, Instruction *I) {
return std::make_shared<ComplexDeinterleavingCompositeNode>(Operation, R,
I);
}
NodePtr submitCompositeNode(NodePtr Node) {
CompositeNodes.push_back(Node);
AllInstructions.insert(Node->Real);
AllInstructions.insert(Node->Imag);
for (auto *I : Node->InternalInstructions)
AllInstructions.insert(I);
return Node;
}
NodePtr getContainingComposite(Value *R, Value *I) {
for (const auto &CN : CompositeNodes) {
if (CN->Real == R && CN->Imag == I)
return CN;
}
return nullptr;
}
/// Identifies a complex partial multiply pattern and its rotation, based on
/// the following patterns
///
/// 0: r: cr + ar * br
/// i: ci + ar * bi
/// 90: r: cr - ai * bi
/// i: ci + ai * br
/// 180: r: cr - ar * br
/// i: ci - ar * bi
/// 270: r: cr + ai * bi
/// i: ci - ai * br
NodePtr identifyPartialMul(Instruction *Real, Instruction *Imag);
/// Identify the other branch of a Partial Mul, taking the CommonOperandI that
/// is partially known from identifyPartialMul, filling in the other half of
/// the complex pair.
NodePtr identifyNodeWithImplicitAdd(
Instruction *I, Instruction *J,
std::pair<Instruction *, Instruction *> &CommonOperandI);
/// Identifies a complex add pattern and its rotation, based on the following
/// patterns.
///
/// 90: r: ar - bi
/// i: ai + br
/// 270: r: ar + bi
/// i: ai - br
NodePtr identifyAdd(Instruction *Real, Instruction *Imag);
NodePtr identifyNode(Instruction *I, Instruction *J);
Value *replaceNode(RawNodePtr Node);
public:
void dump() { dump(dbgs()); }
void dump(raw_ostream &OS) {
for (const auto &Node : CompositeNodes)
Node->dump(OS);
}
/// Returns false if the deinterleaving operation should be cancelled for the
/// current graph.
bool identifyNodes(Instruction *RootI);
/// Perform the actual replacement of the underlying instruction graph.
/// Returns false if the deinterleaving operation should be cancelled for the
/// current graph.
void replaceNodes();
};
class ComplexDeinterleaving {
public:
ComplexDeinterleaving(const TargetLowering *tl, const TargetLibraryInfo *tli)
: TL(tl), TLI(tli) {}
bool runOnFunction(Function &F);
private:
bool evaluateBasicBlock(BasicBlock *B);
const TargetLowering *TL = nullptr;
const TargetLibraryInfo *TLI = nullptr;
};
} // namespace
char ComplexDeinterleavingLegacyPass::ID = 0;
INITIALIZE_PASS_BEGIN(ComplexDeinterleavingLegacyPass, DEBUG_TYPE,
"Complex Deinterleaving", false, false)
INITIALIZE_PASS_END(ComplexDeinterleavingLegacyPass, DEBUG_TYPE,
"Complex Deinterleaving", false, false)
PreservedAnalyses ComplexDeinterleavingPass::run(Function &F,
FunctionAnalysisManager &AM) {
const TargetLowering *TL = TM->getSubtargetImpl(F)->getTargetLowering();
auto &TLI = AM.getResult<llvm::TargetLibraryAnalysis>(F);
if (!ComplexDeinterleaving(TL, &TLI).runOnFunction(F))
return PreservedAnalyses::all();
PreservedAnalyses PA;
PA.preserve<FunctionAnalysisManagerModuleProxy>();
return PA;
}
FunctionPass *llvm::createComplexDeinterleavingPass(const TargetMachine *TM) {
return new ComplexDeinterleavingLegacyPass(TM);
}
bool ComplexDeinterleavingLegacyPass::runOnFunction(Function &F) {
const auto *TL = TM->getSubtargetImpl(F)->getTargetLowering();
auto TLI = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F);
return ComplexDeinterleaving(TL, &TLI).runOnFunction(F);
}
bool ComplexDeinterleaving::runOnFunction(Function &F) {
if (!ComplexDeinterleavingEnabled) {
LLVM_DEBUG(
dbgs() << "Complex deinterleaving has been explicitly disabled.\n");
return false;
}
if (!TL->isComplexDeinterleavingSupported()) {
LLVM_DEBUG(
dbgs() << "Complex deinterleaving has been disabled, target does "
"not support lowering of complex number operations.\n");
return false;
}
bool Changed = false;
for (auto &B : F)
Changed |= evaluateBasicBlock(&B);
return Changed;
}
static bool isInterleavingMask(ArrayRef<int> Mask) {
// If the size is not even, it's not an interleaving mask
if ((Mask.size() & 1))
return false;
int HalfNumElements = Mask.size() / 2;
for (int Idx = 0; Idx < HalfNumElements; ++Idx) {
int MaskIdx = Idx * 2;
if (Mask[MaskIdx] != Idx || Mask[MaskIdx + 1] != (Idx + HalfNumElements))
return false;
}
return true;
}
static bool isDeinterleavingMask(ArrayRef<int> Mask) {
int Offset = Mask[0];
int HalfNumElements = Mask.size() / 2;
for (int Idx = 1; Idx < HalfNumElements; ++Idx) {
if (Mask[Idx] != (Idx * 2) + Offset)
return false;
}
return true;
}
bool ComplexDeinterleaving::evaluateBasicBlock(BasicBlock *B) {
bool Changed = false;
SmallVector<Instruction *> DeadInstrRoots;
for (auto &I : *B) {
auto *SVI = dyn_cast<ShuffleVectorInst>(&I);
if (!SVI)
continue;
// Look for a shufflevector that takes separate vectors of the real and
// imaginary components and recombines them into a single vector.
if (!isInterleavingMask(SVI->getShuffleMask()))
continue;
ComplexDeinterleavingGraph Graph(TL);
if (!Graph.identifyNodes(SVI))
continue;
Graph.replaceNodes();
DeadInstrRoots.push_back(SVI);
Changed = true;
}
for (const auto &I : DeadInstrRoots) {
if (!I || I->getParent() == nullptr)
continue;
llvm::RecursivelyDeleteTriviallyDeadInstructions(I, TLI);
}
return Changed;
}
ComplexDeinterleavingGraph::NodePtr
ComplexDeinterleavingGraph::identifyNodeWithImplicitAdd(
Instruction *Real, Instruction *Imag,
std::pair<Instruction *, Instruction *> &PartialMatch) {
LLVM_DEBUG(dbgs() << "identifyNodeWithImplicitAdd " << *Real << " / " << *Imag
<< "\n");
if (!Real->hasOneUse() || !Imag->hasOneUse()) {
LLVM_DEBUG(dbgs() << " - Mul operand has multiple uses.\n");
return nullptr;
}
if (Real->getOpcode() != Instruction::FMul ||
Imag->getOpcode() != Instruction::FMul) {
LLVM_DEBUG(dbgs() << " - Real or imaginary instruction is not fmul\n");
return nullptr;
}
Instruction *R0 = dyn_cast<Instruction>(Real->getOperand(0));
Instruction *R1 = dyn_cast<Instruction>(Real->getOperand(1));
Instruction *I0 = dyn_cast<Instruction>(Imag->getOperand(0));
Instruction *I1 = dyn_cast<Instruction>(Imag->getOperand(1));
if (!R0 || !R1 || !I0 || !I1) {
LLVM_DEBUG(dbgs() << " - Mul operand not Instruction\n");
return nullptr;
}
// A +/+ has a rotation of 0. If any of the operands are fneg, we flip the
// rotations and use the operand.
unsigned Negs = 0;
SmallVector<Instruction *> FNegs;
if (R0->getOpcode() == Instruction::FNeg ||
R1->getOpcode() == Instruction::FNeg) {
Negs |= 1;
if (R0->getOpcode() == Instruction::FNeg) {
FNegs.push_back(R0);
R0 = dyn_cast<Instruction>(R0->getOperand(0));
} else {
FNegs.push_back(R1);
R1 = dyn_cast<Instruction>(R1->getOperand(0));
}
if (!R0 || !R1)
return nullptr;
}
if (I0->getOpcode() == Instruction::FNeg ||
I1->getOpcode() == Instruction::FNeg) {
Negs |= 2;
Negs ^= 1;
if (I0->getOpcode() == Instruction::FNeg) {
FNegs.push_back(I0);
I0 = dyn_cast<Instruction>(I0->getOperand(0));
} else {
FNegs.push_back(I1);
I1 = dyn_cast<Instruction>(I1->getOperand(0));
}
if (!I0 || !I1)
return nullptr;
}
ComplexDeinterleavingRotation Rotation = (ComplexDeinterleavingRotation)Negs;
Instruction *CommonOperand;
Instruction *UncommonRealOp;
Instruction *UncommonImagOp;
if (R0 == I0 || R0 == I1) {
CommonOperand = R0;
UncommonRealOp = R1;
} else if (R1 == I0 || R1 == I1) {
CommonOperand = R1;
UncommonRealOp = R0;
} else {
LLVM_DEBUG(dbgs() << " - No equal operand\n");
return nullptr;
}
UncommonImagOp = (CommonOperand == I0) ? I1 : I0;
if (Rotation == ComplexDeinterleavingRotation::Rotation_90 ||
Rotation == ComplexDeinterleavingRotation::Rotation_270)
std::swap(UncommonRealOp, UncommonImagOp);
// Between identifyPartialMul and here we need to have found a complete valid
// pair from the CommonOperand of each part.
if (Rotation == ComplexDeinterleavingRotation::Rotation_0 ||
Rotation == ComplexDeinterleavingRotation::Rotation_180)
PartialMatch.first = CommonOperand;
else
PartialMatch.second = CommonOperand;
if (!PartialMatch.first || !PartialMatch.second) {
LLVM_DEBUG(dbgs() << " - Incomplete partial match\n");
return nullptr;
}
NodePtr CommonNode = identifyNode(PartialMatch.first, PartialMatch.second);
if (!CommonNode) {
LLVM_DEBUG(dbgs() << " - No CommonNode identified\n");
return nullptr;
}
NodePtr UncommonNode = identifyNode(UncommonRealOp, UncommonImagOp);
if (!UncommonNode) {
LLVM_DEBUG(dbgs() << " - No UncommonNode identified\n");
return nullptr;
}
NodePtr Node = prepareCompositeNode(
ComplexDeinterleavingOperation::CMulPartial, Real, Imag);
Node->Rotation = Rotation;
Node->addOperand(CommonNode);
Node->addOperand(UncommonNode);
Node->InternalInstructions.append(FNegs);
return submitCompositeNode(Node);
}
ComplexDeinterleavingGraph::NodePtr
ComplexDeinterleavingGraph::identifyPartialMul(Instruction *Real,
Instruction *Imag) {
LLVM_DEBUG(dbgs() << "identifyPartialMul " << *Real << " / " << *Imag
<< "\n");
// Determine rotation
ComplexDeinterleavingRotation Rotation;
if (Real->getOpcode() == Instruction::FAdd &&
Imag->getOpcode() == Instruction::FAdd)
Rotation = ComplexDeinterleavingRotation::Rotation_0;
else if (Real->getOpcode() == Instruction::FSub &&
Imag->getOpcode() == Instruction::FAdd)
Rotation = ComplexDeinterleavingRotation::Rotation_90;
else if (Real->getOpcode() == Instruction::FSub &&
Imag->getOpcode() == Instruction::FSub)
Rotation = ComplexDeinterleavingRotation::Rotation_180;
else if (Real->getOpcode() == Instruction::FAdd &&
Imag->getOpcode() == Instruction::FSub)
Rotation = ComplexDeinterleavingRotation::Rotation_270;
else {
LLVM_DEBUG(dbgs() << " - Unhandled rotation.\n");
return nullptr;
}
if (!Real->getFastMathFlags().allowContract() ||
!Imag->getFastMathFlags().allowContract()) {
LLVM_DEBUG(dbgs() << " - Contract is missing from the FastMath flags.\n");
return nullptr;
}
Value *CR = Real->getOperand(0);
Instruction *RealMulI = dyn_cast<Instruction>(Real->getOperand(1));
if (!RealMulI)
return nullptr;
Value *CI = Imag->getOperand(0);
Instruction *ImagMulI = dyn_cast<Instruction>(Imag->getOperand(1));
if (!ImagMulI)
return nullptr;
if (!RealMulI->hasOneUse() || !ImagMulI->hasOneUse()) {
LLVM_DEBUG(dbgs() << " - Mul instruction has multiple uses\n");
return nullptr;
}
Instruction *R0 = dyn_cast<Instruction>(RealMulI->getOperand(0));
Instruction *R1 = dyn_cast<Instruction>(RealMulI->getOperand(1));
Instruction *I0 = dyn_cast<Instruction>(ImagMulI->getOperand(0));
Instruction *I1 = dyn_cast<Instruction>(ImagMulI->getOperand(1));
if (!R0 || !R1 || !I0 || !I1) {
LLVM_DEBUG(dbgs() << " - Mul operand not Instruction\n");
return nullptr;
}
Instruction *CommonOperand;
Instruction *UncommonRealOp;
Instruction *UncommonImagOp;
if (R0 == I0 || R0 == I1) {
CommonOperand = R0;
UncommonRealOp = R1;
} else if (R1 == I0 || R1 == I1) {
CommonOperand = R1;
UncommonRealOp = R0;
} else {
LLVM_DEBUG(dbgs() << " - No equal operand\n");
return nullptr;
}
UncommonImagOp = (CommonOperand == I0) ? I1 : I0;
if (Rotation == ComplexDeinterleavingRotation::Rotation_90 ||
Rotation == ComplexDeinterleavingRotation::Rotation_270)
std::swap(UncommonRealOp, UncommonImagOp);
std::pair<Instruction *, Instruction *> PartialMatch(
(Rotation == ComplexDeinterleavingRotation::Rotation_0 ||
Rotation == ComplexDeinterleavingRotation::Rotation_180)
? CommonOperand
: nullptr,
(Rotation == ComplexDeinterleavingRotation::Rotation_90 ||
Rotation == ComplexDeinterleavingRotation::Rotation_270)
? CommonOperand
: nullptr);
NodePtr CNode = identifyNodeWithImplicitAdd(
cast<Instruction>(CR), cast<Instruction>(CI), PartialMatch);
if (!CNode) {
LLVM_DEBUG(dbgs() << " - No cnode identified\n");
return nullptr;
}
NodePtr UncommonRes = identifyNode(UncommonRealOp, UncommonImagOp);
if (!UncommonRes) {
LLVM_DEBUG(dbgs() << " - No UncommonRes identified\n");
return nullptr;
}
assert(PartialMatch.first && PartialMatch.second);
NodePtr CommonRes = identifyNode(PartialMatch.first, PartialMatch.second);
if (!CommonRes) {
LLVM_DEBUG(dbgs() << " - No CommonRes identified\n");
return nullptr;
}
NodePtr Node = prepareCompositeNode(
ComplexDeinterleavingOperation::CMulPartial, Real, Imag);
Node->addInstruction(RealMulI);
Node->addInstruction(ImagMulI);
Node->Rotation = Rotation;
Node->addOperand(CommonRes);
Node->addOperand(UncommonRes);
Node->addOperand(CNode);
return submitCompositeNode(Node);
}
ComplexDeinterleavingGraph::NodePtr
ComplexDeinterleavingGraph::identifyAdd(Instruction *Real, Instruction *Imag) {
LLVM_DEBUG(dbgs() << "identifyAdd " << *Real << " / " << *Imag << "\n");
// Determine rotation
ComplexDeinterleavingRotation Rotation;
if ((Real->getOpcode() == Instruction::FSub &&
Imag->getOpcode() == Instruction::FAdd) ||
(Real->getOpcode() == Instruction::Sub &&
Imag->getOpcode() == Instruction::Add))
Rotation = ComplexDeinterleavingRotation::Rotation_90;
else if ((Real->getOpcode() == Instruction::FAdd &&
Imag->getOpcode() == Instruction::FSub) ||
(Real->getOpcode() == Instruction::Add &&
Imag->getOpcode() == Instruction::Sub))
Rotation = ComplexDeinterleavingRotation::Rotation_270;
else {
LLVM_DEBUG(dbgs() << " - Unhandled case, rotation is not assigned.\n");
return nullptr;
}
auto *AR = dyn_cast<Instruction>(Real->getOperand(0));
auto *BI = dyn_cast<Instruction>(Real->getOperand(1));
auto *AI = dyn_cast<Instruction>(Imag->getOperand(0));
auto *BR = dyn_cast<Instruction>(Imag->getOperand(1));
if (!AR || !AI || !BR || !BI) {
LLVM_DEBUG(dbgs() << " - Not all operands are instructions.\n");
return nullptr;
}
NodePtr ResA = identifyNode(AR, AI);
if (!ResA) {
LLVM_DEBUG(dbgs() << " - AR/AI is not identified as a composite node.\n");
return nullptr;
}
NodePtr ResB = identifyNode(BR, BI);
if (!ResB) {
LLVM_DEBUG(dbgs() << " - BR/BI is not identified as a composite node.\n");
return nullptr;
}
NodePtr Node =
prepareCompositeNode(ComplexDeinterleavingOperation::CAdd, Real, Imag);
Node->Rotation = Rotation;
Node->addOperand(ResA);
Node->addOperand(ResB);
return submitCompositeNode(Node);
}
static bool isInstructionPairAdd(Instruction *A, Instruction *B) {
unsigned OpcA = A->getOpcode();
unsigned OpcB = B->getOpcode();
return (OpcA == Instruction::FSub && OpcB == Instruction::FAdd) ||
(OpcA == Instruction::FAdd && OpcB == Instruction::FSub) ||
(OpcA == Instruction::Sub && OpcB == Instruction::Add) ||
(OpcA == Instruction::Add && OpcB == Instruction::Sub);
}
static bool isInstructionPairMul(Instruction *A, Instruction *B) {
auto Pattern =
m_BinOp(m_FMul(m_Value(), m_Value()), m_FMul(m_Value(), m_Value()));
return match(A, Pattern) && match(B, Pattern);
}
ComplexDeinterleavingGraph::NodePtr
ComplexDeinterleavingGraph::identifyNode(Instruction *Real, Instruction *Imag) {
LLVM_DEBUG(dbgs() << "identifyNode on " << *Real << " / " << *Imag << "\n");
if (NodePtr CN = getContainingComposite(Real, Imag)) {
LLVM_DEBUG(dbgs() << " - Folding to existing node\n");
return CN;
}
auto *RealShuffle = dyn_cast<ShuffleVectorInst>(Real);
auto *ImagShuffle = dyn_cast<ShuffleVectorInst>(Imag);
if (RealShuffle && ImagShuffle) {
Value *RealOp1 = RealShuffle->getOperand(1);
if (!isa<UndefValue>(RealOp1) && !isa<ConstantAggregateZero>(RealOp1)) {
LLVM_DEBUG(dbgs() << " - RealOp1 is not undef or zero.\n");
return nullptr;
}
Value *ImagOp1 = ImagShuffle->getOperand(1);
if (!isa<UndefValue>(ImagOp1) && !isa<ConstantAggregateZero>(ImagOp1)) {
LLVM_DEBUG(dbgs() << " - ImagOp1 is not undef or zero.\n");
return nullptr;
}
Value *RealOp0 = RealShuffle->getOperand(0);
Value *ImagOp0 = ImagShuffle->getOperand(0);
if (RealOp0 != ImagOp0) {
LLVM_DEBUG(dbgs() << " - Shuffle operands are not equal.\n");
return nullptr;
}
ArrayRef<int> RealMask = RealShuffle->getShuffleMask();
ArrayRef<int> ImagMask = ImagShuffle->getShuffleMask();
if (!isDeinterleavingMask(RealMask) || !isDeinterleavingMask(ImagMask)) {
LLVM_DEBUG(dbgs() << " - Masks are not deinterleaving.\n");
return nullptr;
}
if (RealMask[0] != 0 || ImagMask[0] != 1) {
LLVM_DEBUG(dbgs() << " - Masks do not have the correct initial value.\n");
return nullptr;
}
// Type checking, the shuffle type should be a vector type of the same
// scalar type, but half the size
auto CheckType = [&](ShuffleVectorInst *Shuffle) {
Value *Op = Shuffle->getOperand(0);
auto *ShuffleTy = cast<FixedVectorType>(Shuffle->getType());
auto *OpTy = cast<FixedVectorType>(Op->getType());
if (OpTy->getScalarType() != ShuffleTy->getScalarType())
return false;
if ((ShuffleTy->getNumElements() * 2) != OpTy->getNumElements())
return false;
return true;
};
auto CheckDeinterleavingShuffle = [&](ShuffleVectorInst *Shuffle) -> bool {
if (!CheckType(Shuffle))
return false;
ArrayRef<int> Mask = Shuffle->getShuffleMask();
int Last = *Mask.rbegin();
Value *Op = Shuffle->getOperand(0);
auto *OpTy = cast<FixedVectorType>(Op->getType());
int NumElements = OpTy->getNumElements();
// Ensure that the deinterleaving shuffle only pulls from the first
// shuffle operand.
return Last < NumElements;
};
if (RealShuffle->getType() != ImagShuffle->getType()) {
LLVM_DEBUG(dbgs() << " - Shuffle types aren't equal.\n");
return nullptr;
}
if (!CheckDeinterleavingShuffle(RealShuffle)) {
LLVM_DEBUG(dbgs() << " - RealShuffle is invalid type.\n");
return nullptr;
}
if (!CheckDeinterleavingShuffle(ImagShuffle)) {
LLVM_DEBUG(dbgs() << " - ImagShuffle is invalid type.\n");
return nullptr;
}
NodePtr PlaceholderNode =
prepareCompositeNode(llvm::ComplexDeinterleavingOperation::Shuffle,
RealShuffle, ImagShuffle);
PlaceholderNode->ReplacementNode = RealShuffle->getOperand(0);
return submitCompositeNode(PlaceholderNode);
}
if (RealShuffle || ImagShuffle)
return nullptr;
auto *VTy = cast<FixedVectorType>(Real->getType());
auto *NewVTy =
FixedVectorType::get(VTy->getScalarType(), VTy->getNumElements() * 2);
if (TL->isComplexDeinterleavingOperationSupported(
ComplexDeinterleavingOperation::CMulPartial, NewVTy) &&
isInstructionPairMul(Real, Imag)) {
return identifyPartialMul(Real, Imag);
}
if (TL->isComplexDeinterleavingOperationSupported(
ComplexDeinterleavingOperation::CAdd, NewVTy) &&
isInstructionPairAdd(Real, Imag)) {
return identifyAdd(Real, Imag);
}
return nullptr;
}
bool ComplexDeinterleavingGraph::identifyNodes(Instruction *RootI) {
Instruction *Real;
Instruction *Imag;
if (!match(RootI, m_Shuffle(m_Instruction(Real), m_Instruction(Imag))))
return false;
RootValue = RootI;
AllInstructions.insert(RootI);
RootNode = identifyNode(Real, Imag);
LLVM_DEBUG({
Function *F = RootI->getFunction();
BasicBlock *B = RootI->getParent();
dbgs() << "Complex deinterleaving graph for " << F->getName()
<< "::" << B->getName() << ".\n";
dump(dbgs());
dbgs() << "\n";
});
// Check all instructions have internal uses
for (const auto &Node : CompositeNodes) {
if (!Node->hasAllInternalUses(AllInstructions)) {
LLVM_DEBUG(dbgs() << " - Invalid internal uses\n");
return false;
}
}
return RootNode != nullptr;
}
Value *ComplexDeinterleavingGraph::replaceNode(
ComplexDeinterleavingGraph::RawNodePtr Node) {
if (Node->ReplacementNode)
return Node->ReplacementNode;
Value *Input0 = replaceNode(Node->Operands[0]);
Value *Input1 = replaceNode(Node->Operands[1]);
Value *Accumulator =
Node->Operands.size() > 2 ? replaceNode(Node->Operands[2]) : nullptr;
assert(Input0->getType() == Input1->getType() &&
"Node inputs need to be of the same type");
Node->ReplacementNode = TL->createComplexDeinterleavingIR(
Node->Real, Node->Operation, Node->Rotation, Input0, Input1, Accumulator);
assert(Node->ReplacementNode && "Target failed to create Intrinsic call.");
NumComplexTransformations += 1;
return Node->ReplacementNode;
}
void ComplexDeinterleavingGraph::replaceNodes() {
Value *R = replaceNode(RootNode.get());
assert(R && "Unable to find replacement for RootValue");
RootValue->replaceAllUsesWith(R);
}
bool ComplexDeinterleavingCompositeNode::hasAllInternalUses(
SmallPtrSet<Instruction *, 16> &AllInstructions) {
if (Operation == ComplexDeinterleavingOperation::Shuffle)
return true;
for (auto *User : Real->users()) {
if (!AllInstructions.contains(cast<Instruction>(User)))
return false;
}
for (auto *User : Imag->users()) {
if (!AllInstructions.contains(cast<Instruction>(User)))
return false;
}
for (auto *I : InternalInstructions) {
for (auto *User : I->users()) {
if (!AllInstructions.contains(cast<Instruction>(User)))
return false;
}
}
return true;
}