#ifndef LLVM_CODEGEN_GLOBALISEL_LEGALIZATIONARTIFACTCOMBINER_H
#define LLVM_CODEGEN_GLOBALISEL_LEGALIZATIONARTIFACTCOMBINER_H
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
#include "llvm/CodeGen/GlobalISel/Legalizer.h"
#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/GlobalISel/Utils.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Register.h"
#include "llvm/IR/Constants.h"
#include "llvm/Support/Debug.h"
#define DEBUG_TYPE "legalizer"
namespace llvm {
class LegalizationArtifactCombiner {
MachineIRBuilder &Builder;
MachineRegisterInfo &MRI;
const LegalizerInfo &LI;
static bool isArtifactCast(unsigned Opc) {
switch (Opc) {
case TargetOpcode::G_TRUNC:
case TargetOpcode::G_SEXT:
case TargetOpcode::G_ZEXT:
case TargetOpcode::G_ANYEXT:
return true;
default:
return false;
}
}
public:
LegalizationArtifactCombiner(MachineIRBuilder &B, MachineRegisterInfo &MRI,
const LegalizerInfo &LI)
: Builder(B), MRI(MRI), LI(LI) {}
bool tryCombineAnyExt(MachineInstr &MI,
SmallVectorImpl<MachineInstr *> &DeadInsts,
SmallVectorImpl<Register> &UpdatedDefs,
GISelObserverWrapper &Observer) {
using namespace llvm::MIPatternMatch;
assert(MI.getOpcode() == TargetOpcode::G_ANYEXT);
Builder.setInstrAndDebugLoc(MI);
Register DstReg = MI.getOperand(0).getReg();
Register SrcReg = lookThroughCopyInstrs(MI.getOperand(1).getReg());
Register TruncSrc;
if (mi_match(SrcReg, MRI, m_GTrunc(m_Reg(TruncSrc)))) {
LLVM_DEBUG(dbgs() << ".. Combine MI: " << MI;);
if (MRI.getType(DstReg) == MRI.getType(TruncSrc))
replaceRegOrBuildCopy(DstReg, TruncSrc, MRI, Builder, UpdatedDefs,
Observer);
else
Builder.buildAnyExtOrTrunc(DstReg, TruncSrc);
UpdatedDefs.push_back(DstReg);
markInstAndDefDead(MI, *MRI.getVRegDef(SrcReg), DeadInsts);
return true;
}
Register ExtSrc;
MachineInstr *ExtMI;
if (mi_match(SrcReg, MRI,
m_all_of(m_MInstr(ExtMI), m_any_of(m_GAnyExt(m_Reg(ExtSrc)),
m_GSExt(m_Reg(ExtSrc)),
m_GZExt(m_Reg(ExtSrc)))))) {
Builder.buildInstr(ExtMI->getOpcode(), {DstReg}, {ExtSrc});
UpdatedDefs.push_back(DstReg);
markInstAndDefDead(MI, *ExtMI, DeadInsts);
return true;
}
auto *SrcMI = MRI.getVRegDef(SrcReg);
if (SrcMI->getOpcode() == TargetOpcode::G_CONSTANT) {
const LLT DstTy = MRI.getType(DstReg);
if (isInstLegal({TargetOpcode::G_CONSTANT, {DstTy}})) {
auto &CstVal = SrcMI->getOperand(1);
Builder.buildConstant(
DstReg, CstVal.getCImm()->getValue().sext(DstTy.getSizeInBits()));
UpdatedDefs.push_back(DstReg);
markInstAndDefDead(MI, *SrcMI, DeadInsts);
return true;
}
}
return tryFoldImplicitDef(MI, DeadInsts, UpdatedDefs);
}
bool tryCombineZExt(MachineInstr &MI,
SmallVectorImpl<MachineInstr *> &DeadInsts,
SmallVectorImpl<Register> &UpdatedDefs,
GISelObserverWrapper &Observer) {
using namespace llvm::MIPatternMatch;
assert(MI.getOpcode() == TargetOpcode::G_ZEXT);
Builder.setInstrAndDebugLoc(MI);
Register DstReg = MI.getOperand(0).getReg();
Register SrcReg = lookThroughCopyInstrs(MI.getOperand(1).getReg());
Register TruncSrc;
Register SextSrc;
if (mi_match(SrcReg, MRI, m_GTrunc(m_Reg(TruncSrc))) ||
mi_match(SrcReg, MRI, m_GSExt(m_Reg(SextSrc)))) {
LLT DstTy = MRI.getType(DstReg);
if (isInstUnsupported({TargetOpcode::G_AND, {DstTy}}) ||
isConstantUnsupported(DstTy))
return false;
LLVM_DEBUG(dbgs() << ".. Combine MI: " << MI;);
LLT SrcTy = MRI.getType(SrcReg);
APInt MaskVal = APInt::getAllOnes(SrcTy.getScalarSizeInBits());
auto Mask = Builder.buildConstant(
DstTy, MaskVal.zext(DstTy.getScalarSizeInBits()));
if (SextSrc && (DstTy != MRI.getType(SextSrc)))
SextSrc = Builder.buildSExtOrTrunc(DstTy, SextSrc).getReg(0);
if (TruncSrc && (DstTy != MRI.getType(TruncSrc)))
TruncSrc = Builder.buildAnyExtOrTrunc(DstTy, TruncSrc).getReg(0);
Builder.buildAnd(DstReg, SextSrc ? SextSrc : TruncSrc, Mask);
markInstAndDefDead(MI, *MRI.getVRegDef(SrcReg), DeadInsts);
return true;
}
Register ZextSrc;
if (mi_match(SrcReg, MRI, m_GZExt(m_Reg(ZextSrc)))) {
LLVM_DEBUG(dbgs() << ".. Combine MI: " << MI);
Observer.changingInstr(MI);
MI.getOperand(1).setReg(ZextSrc);
Observer.changedInstr(MI);
UpdatedDefs.push_back(DstReg);
markDefDead(MI, *MRI.getVRegDef(SrcReg), DeadInsts);
return true;
}
auto *SrcMI = MRI.getVRegDef(SrcReg);
if (SrcMI->getOpcode() == TargetOpcode::G_CONSTANT) {
const LLT DstTy = MRI.getType(DstReg);
if (isInstLegal({TargetOpcode::G_CONSTANT, {DstTy}})) {
auto &CstVal = SrcMI->getOperand(1);
Builder.buildConstant(
DstReg, CstVal.getCImm()->getValue().zext(DstTy.getSizeInBits()));
UpdatedDefs.push_back(DstReg);
markInstAndDefDead(MI, *SrcMI, DeadInsts);
return true;
}
}
return tryFoldImplicitDef(MI, DeadInsts, UpdatedDefs);
}
bool tryCombineSExt(MachineInstr &MI,
SmallVectorImpl<MachineInstr *> &DeadInsts,
SmallVectorImpl<Register> &UpdatedDefs) {
using namespace llvm::MIPatternMatch;
assert(MI.getOpcode() == TargetOpcode::G_SEXT);
Builder.setInstrAndDebugLoc(MI);
Register DstReg = MI.getOperand(0).getReg();
Register SrcReg = lookThroughCopyInstrs(MI.getOperand(1).getReg());
Register TruncSrc;
if (mi_match(SrcReg, MRI, m_GTrunc(m_Reg(TruncSrc)))) {
LLT DstTy = MRI.getType(DstReg);
if (isInstUnsupported({TargetOpcode::G_SEXT_INREG, {DstTy}}))
return false;
LLVM_DEBUG(dbgs() << ".. Combine MI: " << MI;);
LLT SrcTy = MRI.getType(SrcReg);
uint64_t SizeInBits = SrcTy.getScalarSizeInBits();
if (DstTy != MRI.getType(TruncSrc))
TruncSrc = Builder.buildAnyExtOrTrunc(DstTy, TruncSrc).getReg(0);
Builder.buildSExtInReg(DstReg, TruncSrc, SizeInBits);
markInstAndDefDead(MI, *MRI.getVRegDef(SrcReg), DeadInsts);
return true;
}
Register ExtSrc;
MachineInstr *ExtMI;
if (mi_match(SrcReg, MRI,
m_all_of(m_MInstr(ExtMI), m_any_of(m_GZExt(m_Reg(ExtSrc)),
m_GSExt(m_Reg(ExtSrc)))))) {
LLVM_DEBUG(dbgs() << ".. Combine MI: " << MI);
Builder.buildInstr(ExtMI->getOpcode(), {DstReg}, {ExtSrc});
UpdatedDefs.push_back(DstReg);
markInstAndDefDead(MI, *MRI.getVRegDef(SrcReg), DeadInsts);
return true;
}
auto *SrcMI = MRI.getVRegDef(SrcReg);
if (SrcMI->getOpcode() == TargetOpcode::G_CONSTANT) {
const LLT DstTy = MRI.getType(DstReg);
if (isInstLegal({TargetOpcode::G_CONSTANT, {DstTy}})) {
auto &CstVal = SrcMI->getOperand(1);
Builder.buildConstant(
DstReg, CstVal.getCImm()->getValue().sext(DstTy.getSizeInBits()));
UpdatedDefs.push_back(DstReg);
markInstAndDefDead(MI, *SrcMI, DeadInsts);
return true;
}
}
return tryFoldImplicitDef(MI, DeadInsts, UpdatedDefs);
}
bool tryCombineTrunc(MachineInstr &MI,
SmallVectorImpl<MachineInstr *> &DeadInsts,
SmallVectorImpl<Register> &UpdatedDefs,
GISelObserverWrapper &Observer) {
using namespace llvm::MIPatternMatch;
assert(MI.getOpcode() == TargetOpcode::G_TRUNC);
Builder.setInstr(MI);
Register DstReg = MI.getOperand(0).getReg();
Register SrcReg = lookThroughCopyInstrs(MI.getOperand(1).getReg());
auto *SrcMI = MRI.getVRegDef(SrcReg);
if (SrcMI->getOpcode() == TargetOpcode::G_CONSTANT) {
const LLT DstTy = MRI.getType(DstReg);
if (isInstLegal({TargetOpcode::G_CONSTANT, {DstTy}})) {
auto &CstVal = SrcMI->getOperand(1);
Builder.buildConstant(
DstReg, CstVal.getCImm()->getValue().trunc(DstTy.getSizeInBits()));
UpdatedDefs.push_back(DstReg);
markInstAndDefDead(MI, *SrcMI, DeadInsts);
return true;
}
}
if (auto *SrcMerge = dyn_cast<GMerge>(SrcMI)) {
const Register MergeSrcReg = SrcMerge->getSourceReg(0);
const LLT MergeSrcTy = MRI.getType(MergeSrcReg);
const LLT DstTy = MRI.getType(DstReg);
const unsigned DstSize = DstTy.getSizeInBits();
const unsigned MergeSrcSize = MergeSrcTy.getSizeInBits();
if (!DstTy.isScalar() || !MergeSrcTy.isScalar())
return false;
if (DstSize < MergeSrcSize) {
if (isInstUnsupported({TargetOpcode::G_TRUNC, {DstTy, MergeSrcTy}}))
return false;
LLVM_DEBUG(dbgs() << "Combining G_TRUNC(G_MERGE_VALUES) to G_TRUNC: "
<< MI);
Builder.buildTrunc(DstReg, MergeSrcReg);
UpdatedDefs.push_back(DstReg);
} else if (DstSize == MergeSrcSize) {
LLVM_DEBUG(
dbgs() << "Replacing G_TRUNC(G_MERGE_VALUES) with merge input: "
<< MI);
replaceRegOrBuildCopy(DstReg, MergeSrcReg, MRI, Builder, UpdatedDefs,
Observer);
} else if (DstSize % MergeSrcSize == 0) {
if (isInstUnsupported(
{TargetOpcode::G_MERGE_VALUES, {DstTy, MergeSrcTy}}))
return false;
LLVM_DEBUG(
dbgs() << "Combining G_TRUNC(G_MERGE_VALUES) to G_MERGE_VALUES: "
<< MI);
const unsigned NumSrcs = DstSize / MergeSrcSize;
assert(NumSrcs < SrcMI->getNumOperands() - 1 &&
"trunc(merge) should require less inputs than merge");
SmallVector<Register, 8> SrcRegs(NumSrcs);
for (unsigned i = 0; i < NumSrcs; ++i)
SrcRegs[i] = SrcMerge->getSourceReg(i);
Builder.buildMerge(DstReg, SrcRegs);
UpdatedDefs.push_back(DstReg);
} else {
return false;
}
markInstAndDefDead(MI, *SrcMerge, DeadInsts);
return true;
}
Register TruncSrc;
if (mi_match(SrcReg, MRI, m_GTrunc(m_Reg(TruncSrc)))) {
LLVM_DEBUG(dbgs() << ".. Combine G_TRUNC(G_TRUNC): " << MI);
Builder.buildTrunc(DstReg, TruncSrc);
UpdatedDefs.push_back(DstReg);
markInstAndDefDead(MI, *MRI.getVRegDef(TruncSrc), DeadInsts);
return true;
}
return false;
}
bool tryFoldImplicitDef(MachineInstr &MI,
SmallVectorImpl<MachineInstr *> &DeadInsts,
SmallVectorImpl<Register> &UpdatedDefs) {
unsigned Opcode = MI.getOpcode();
assert(Opcode == TargetOpcode::G_ANYEXT || Opcode == TargetOpcode::G_ZEXT ||
Opcode == TargetOpcode::G_SEXT);
if (MachineInstr *DefMI = getOpcodeDef(TargetOpcode::G_IMPLICIT_DEF,
MI.getOperand(1).getReg(), MRI)) {
Builder.setInstr(MI);
Register DstReg = MI.getOperand(0).getReg();
LLT DstTy = MRI.getType(DstReg);
if (Opcode == TargetOpcode::G_ANYEXT) {
if (!isInstLegal({TargetOpcode::G_IMPLICIT_DEF, {DstTy}}))
return false;
LLVM_DEBUG(dbgs() << ".. Combine G_ANYEXT(G_IMPLICIT_DEF): " << MI;);
Builder.buildInstr(TargetOpcode::G_IMPLICIT_DEF, {DstReg}, {});
UpdatedDefs.push_back(DstReg);
} else {
if (isConstantUnsupported(DstTy))
return false;
LLVM_DEBUG(dbgs() << ".. Combine G_[SZ]EXT(G_IMPLICIT_DEF): " << MI;);
Builder.buildConstant(DstReg, 0);
UpdatedDefs.push_back(DstReg);
}
markInstAndDefDead(MI, *DefMI, DeadInsts);
return true;
}
return false;
}
bool tryFoldUnmergeCast(MachineInstr &MI, MachineInstr &CastMI,
SmallVectorImpl<MachineInstr *> &DeadInsts,
SmallVectorImpl<Register> &UpdatedDefs) {
assert(MI.getOpcode() == TargetOpcode::G_UNMERGE_VALUES);
const unsigned CastOpc = CastMI.getOpcode();
if (!isArtifactCast(CastOpc))
return false;
const unsigned NumDefs = MI.getNumOperands() - 1;
const Register CastSrcReg = CastMI.getOperand(1).getReg();
const LLT CastSrcTy = MRI.getType(CastSrcReg);
const LLT DestTy = MRI.getType(MI.getOperand(0).getReg());
const LLT SrcTy = MRI.getType(MI.getOperand(NumDefs).getReg());
const unsigned CastSrcSize = CastSrcTy.getSizeInBits();
const unsigned DestSize = DestTy.getSizeInBits();
if (CastOpc == TargetOpcode::G_TRUNC) {
if (SrcTy.isVector() && SrcTy.getScalarType() == DestTy.getScalarType()) {
unsigned UnmergeNumElts =
DestTy.isVector() ? CastSrcTy.getNumElements() / NumDefs : 1;
LLT UnmergeTy = CastSrcTy.changeElementCount(
ElementCount::getFixed(UnmergeNumElts));
if (isInstUnsupported(
{TargetOpcode::G_UNMERGE_VALUES, {UnmergeTy, CastSrcTy}}))
return false;
Builder.setInstr(MI);
auto NewUnmerge = Builder.buildUnmerge(UnmergeTy, CastSrcReg);
for (unsigned I = 0; I != NumDefs; ++I) {
Register DefReg = MI.getOperand(I).getReg();
UpdatedDefs.push_back(DefReg);
Builder.buildTrunc(DefReg, NewUnmerge.getReg(I));
}
markInstAndDefDead(MI, CastMI, DeadInsts);
return true;
}
if (CastSrcTy.isScalar() && SrcTy.isScalar() && !DestTy.isVector()) {
if (CastSrcSize % DestSize != 0)
return false;
if (isInstUnsupported(
{TargetOpcode::G_UNMERGE_VALUES, {DestTy, CastSrcTy}}))
return false;
const unsigned NewNumDefs = CastSrcSize / DestSize;
SmallVector<Register, 8> DstRegs(NewNumDefs);
for (unsigned Idx = 0; Idx < NewNumDefs; ++Idx) {
if (Idx < NumDefs)
DstRegs[Idx] = MI.getOperand(Idx).getReg();
else
DstRegs[Idx] = MRI.createGenericVirtualRegister(DestTy);
}
Builder.setInstr(MI);
Builder.buildUnmerge(DstRegs, CastSrcReg);
UpdatedDefs.append(DstRegs.begin(), DstRegs.begin() + NewNumDefs);
markInstAndDefDead(MI, CastMI, DeadInsts);
return true;
}
}
return false;
}
static bool canFoldMergeOpcode(unsigned MergeOp, unsigned ConvertOp,
LLT OpTy, LLT DestTy) {
switch (MergeOp) {
default:
return false;
case TargetOpcode::G_BUILD_VECTOR:
case TargetOpcode::G_MERGE_VALUES:
if (ConvertOp == 0)
return true;
return !DestTy.isVector() && OpTy.isVector() &&
DestTy == OpTy.getElementType();
case TargetOpcode::G_CONCAT_VECTORS: {
if (ConvertOp == 0)
return true;
if (!DestTy.isVector())
return false;
const unsigned OpEltSize = OpTy.getElementType().getSizeInBits();
if (ConvertOp == TargetOpcode::G_TRUNC)
return DestTy.getSizeInBits() <= OpEltSize;
return DestTy.getSizeInBits() >= OpEltSize;
}
}
}
static void replaceRegOrBuildCopy(Register DstReg, Register SrcReg,
MachineRegisterInfo &MRI,
MachineIRBuilder &Builder,
SmallVectorImpl<Register> &UpdatedDefs,
GISelChangeObserver &Observer) {
if (!llvm::canReplaceReg(DstReg, SrcReg, MRI)) {
Builder.buildCopy(DstReg, SrcReg);
UpdatedDefs.push_back(DstReg);
return;
}
SmallVector<MachineInstr *, 4> UseMIs;
for (auto &UseMI : MRI.use_instructions(DstReg)) {
UseMIs.push_back(&UseMI);
Observer.changingInstr(UseMI);
}
MRI.replaceRegWith(DstReg, SrcReg);
UpdatedDefs.push_back(SrcReg);
for (auto *UseMI : UseMIs)
Observer.changedInstr(*UseMI);
}
static unsigned getDefIndex(const MachineInstr &MI, Register SearchDef) {
unsigned DefIdx = 0;
for (const MachineOperand &Def : MI.defs()) {
if (Def.getReg() == SearchDef)
break;
++DefIdx;
}
return DefIdx;
}
class ArtifactValueFinder {
MachineRegisterInfo &MRI;
MachineIRBuilder &MIB;
const LegalizerInfo &LI;
Register CurrentBest = Register();
Register findValueFromConcat(GConcatVectors &Concat, unsigned StartBit,
unsigned Size) {
assert(Size > 0);
Register Src1Reg = Concat.getSourceReg(0);
unsigned SrcSize = MRI.getType(Src1Reg).getSizeInBits();
unsigned StartSrcIdx = (StartBit / SrcSize) + 1;
unsigned InRegOffset = StartBit % SrcSize;
if (InRegOffset + Size > SrcSize)
return CurrentBest;
Register SrcReg = Concat.getReg(StartSrcIdx);
if (InRegOffset == 0 && Size == SrcSize) {
CurrentBest = SrcReg;
return findValueFromDefImpl(SrcReg, 0, Size);
}
return findValueFromDefImpl(SrcReg, InRegOffset, Size);
}
Register findValueFromBuildVector(GBuildVector &BV, unsigned StartBit,
unsigned Size) {
assert(Size > 0);
Register Src1Reg = BV.getSourceReg(0);
unsigned SrcSize = MRI.getType(Src1Reg).getSizeInBits();
unsigned StartSrcIdx = (StartBit / SrcSize) + 1;
unsigned InRegOffset = StartBit % SrcSize;
if (InRegOffset != 0)
return CurrentBest; if (Size < SrcSize)
return CurrentBest;
if (Size > SrcSize) {
if (Size % SrcSize > 0)
return CurrentBest;
unsigned NumSrcsUsed = Size / SrcSize;
if (NumSrcsUsed == BV.getNumSources())
return BV.getReg(0);
LLT SrcTy = MRI.getType(Src1Reg);
LLT NewBVTy = LLT::fixed_vector(NumSrcsUsed, SrcTy);
LegalizeActionStep ActionStep =
LI.getAction({TargetOpcode::G_BUILD_VECTOR, {NewBVTy, SrcTy}});
if (ActionStep.Action != LegalizeActions::Legal)
return CurrentBest;
SmallVector<Register> NewSrcs;
for (unsigned SrcIdx = StartSrcIdx; SrcIdx < StartSrcIdx + NumSrcsUsed;
++SrcIdx)
NewSrcs.push_back(BV.getReg(SrcIdx));
MIB.setInstrAndDebugLoc(BV);
return MIB.buildBuildVector(NewBVTy, NewSrcs).getReg(0);
}
return BV.getReg(StartSrcIdx);
}
Register findValueFromInsert(MachineInstr &MI, unsigned StartBit,
unsigned Size) {
assert(MI.getOpcode() == TargetOpcode::G_INSERT);
assert(Size > 0);
Register ContainerSrcReg = MI.getOperand(1).getReg();
Register InsertedReg = MI.getOperand(2).getReg();
LLT InsertedRegTy = MRI.getType(InsertedReg);
unsigned InsertOffset = MI.getOperand(3).getImm();
unsigned InsertedEndBit = InsertOffset + InsertedRegTy.getSizeInBits();
unsigned EndBit = StartBit + Size;
unsigned NewStartBit;
Register SrcRegToUse;
if (EndBit <= InsertOffset || InsertedEndBit <= StartBit) {
SrcRegToUse = ContainerSrcReg;
NewStartBit = StartBit;
return findValueFromDefImpl(SrcRegToUse, NewStartBit, Size);
}
if (InsertOffset <= StartBit && EndBit <= InsertedEndBit) {
SrcRegToUse = InsertedReg;
NewStartBit = StartBit - InsertOffset;
if (NewStartBit == 0 &&
Size == MRI.getType(SrcRegToUse).getSizeInBits())
CurrentBest = SrcRegToUse;
return findValueFromDefImpl(SrcRegToUse, NewStartBit, Size);
}
return Register();
}
Register findValueFromDefImpl(Register DefReg, unsigned StartBit,
unsigned Size) {
MachineInstr *Def = getDefIgnoringCopies(DefReg, MRI);
switch (Def->getOpcode()) {
case TargetOpcode::G_CONCAT_VECTORS:
return findValueFromConcat(cast<GConcatVectors>(*Def), StartBit, Size);
case TargetOpcode::G_UNMERGE_VALUES: {
unsigned DefStartBit = 0;
unsigned DefSize = MRI.getType(DefReg).getSizeInBits();
for (const auto &MO : Def->defs()) {
if (MO.getReg() == DefReg)
break;
DefStartBit += DefSize;
}
Register SrcReg = Def->getOperand(Def->getNumOperands() - 1).getReg();
Register SrcOriginReg =
findValueFromDefImpl(SrcReg, StartBit + DefStartBit, Size);
if (SrcOriginReg)
return SrcOriginReg;
if (StartBit == 0 && Size == DefSize)
return DefReg;
return CurrentBest;
}
case TargetOpcode::G_BUILD_VECTOR:
return findValueFromBuildVector(cast<GBuildVector>(*Def), StartBit,
Size);
case TargetOpcode::G_INSERT:
return findValueFromInsert(*Def, StartBit, Size);
default:
return CurrentBest;
}
}
public:
ArtifactValueFinder(MachineRegisterInfo &Mri, MachineIRBuilder &Builder,
const LegalizerInfo &Info)
: MRI(Mri), MIB(Builder), LI(Info) {}
Register findValueFromDef(Register DefReg, unsigned StartBit,
unsigned Size) {
CurrentBest = Register();
Register FoundReg = findValueFromDefImpl(DefReg, StartBit, Size);
return FoundReg != DefReg ? FoundReg : Register();
}
bool tryCombineUnmergeDefs(GUnmerge &MI, GISelChangeObserver &Observer,
SmallVectorImpl<Register> &UpdatedDefs) {
unsigned NumDefs = MI.getNumDefs();
LLT DestTy = MRI.getType(MI.getReg(0));
SmallBitVector DeadDefs(NumDefs);
for (unsigned DefIdx = 0; DefIdx < NumDefs; ++DefIdx) {
Register DefReg = MI.getReg(DefIdx);
if (MRI.use_nodbg_empty(DefReg)) {
DeadDefs[DefIdx] = true;
continue;
}
Register FoundVal = findValueFromDef(DefReg, 0, DestTy.getSizeInBits());
if (!FoundVal)
continue;
if (MRI.getType(FoundVal) != DestTy)
continue;
replaceRegOrBuildCopy(DefReg, FoundVal, MRI, MIB, UpdatedDefs,
Observer);
Observer.changingInstr(MI);
MI.getOperand(DefIdx).setReg(DefReg);
Observer.changedInstr(MI);
DeadDefs[DefIdx] = true;
}
return DeadDefs.all();
}
};
bool tryCombineUnmergeValues(GUnmerge &MI,
SmallVectorImpl<MachineInstr *> &DeadInsts,
SmallVectorImpl<Register> &UpdatedDefs,
GISelChangeObserver &Observer) {
unsigned NumDefs = MI.getNumDefs();
Register SrcReg = MI.getSourceReg();
MachineInstr *SrcDef = getDefIgnoringCopies(SrcReg, MRI);
if (!SrcDef)
return false;
LLT OpTy = MRI.getType(SrcReg);
LLT DestTy = MRI.getType(MI.getReg(0));
unsigned SrcDefIdx = getDefIndex(*SrcDef, SrcReg);
Builder.setInstrAndDebugLoc(MI);
ArtifactValueFinder Finder(MRI, Builder, LI);
if (Finder.tryCombineUnmergeDefs(MI, Observer, UpdatedDefs)) {
markInstAndDefDead(MI, *SrcDef, DeadInsts, SrcDefIdx);
return true;
}
if (auto *SrcUnmerge = dyn_cast<GUnmerge>(SrcDef)) {
Register SrcUnmergeSrc = SrcUnmerge->getSourceReg();
LLT SrcUnmergeSrcTy = MRI.getType(SrcUnmergeSrc);
LegalizeActionStep ActionStep = LI.getAction(
{TargetOpcode::G_UNMERGE_VALUES, {OpTy, SrcUnmergeSrcTy}});
switch (ActionStep.Action) {
case LegalizeActions::Lower:
case LegalizeActions::Unsupported:
break;
case LegalizeActions::FewerElements:
case LegalizeActions::NarrowScalar:
if (ActionStep.TypeIdx == 1)
return false;
break;
default:
return false;
}
auto NewUnmerge = Builder.buildUnmerge(DestTy, SrcUnmergeSrc);
for (unsigned I = 0; I != NumDefs; ++I) {
Register Def = MI.getReg(I);
replaceRegOrBuildCopy(Def, NewUnmerge.getReg(SrcDefIdx * NumDefs + I),
MRI, Builder, UpdatedDefs, Observer);
}
markInstAndDefDead(MI, *SrcUnmerge, DeadInsts, SrcDefIdx);
return true;
}
MachineInstr *MergeI = SrcDef;
unsigned ConvertOp = 0;
unsigned SrcOp = SrcDef->getOpcode();
if (isArtifactCast(SrcOp)) {
ConvertOp = SrcOp;
MergeI = getDefIgnoringCopies(SrcDef->getOperand(1).getReg(), MRI);
}
if (!MergeI || !canFoldMergeOpcode(MergeI->getOpcode(),
ConvertOp, OpTy, DestTy)) {
return tryFoldUnmergeCast(MI, *SrcDef, DeadInsts, UpdatedDefs);
}
const unsigned NumMergeRegs = MergeI->getNumOperands() - 1;
if (NumMergeRegs < NumDefs) {
if (NumDefs % NumMergeRegs != 0)
return false;
Builder.setInstr(MI);
const unsigned NewNumDefs = NumDefs / NumMergeRegs;
for (unsigned Idx = 0; Idx < NumMergeRegs; ++Idx) {
SmallVector<Register, 8> DstRegs;
for (unsigned j = 0, DefIdx = Idx * NewNumDefs; j < NewNumDefs;
++j, ++DefIdx)
DstRegs.push_back(MI.getReg(DefIdx));
if (ConvertOp) {
LLT MergeSrcTy = MRI.getType(MergeI->getOperand(1).getReg());
LLT MergeEltTy = MergeSrcTy.divide(NewNumDefs);
SmallVector<Register, 4> TmpRegs(NewNumDefs);
for (unsigned k = 0; k < NewNumDefs; ++k)
TmpRegs[k] = MRI.createGenericVirtualRegister(MergeEltTy);
Builder.buildUnmerge(TmpRegs, MergeI->getOperand(Idx + 1).getReg());
for (unsigned k = 0; k < NewNumDefs; ++k)
Builder.buildInstr(ConvertOp, {DstRegs[k]}, {TmpRegs[k]});
} else {
Builder.buildUnmerge(DstRegs, MergeI->getOperand(Idx + 1).getReg());
}
UpdatedDefs.append(DstRegs.begin(), DstRegs.end());
}
} else if (NumMergeRegs > NumDefs) {
if (ConvertOp != 0 || NumMergeRegs % NumDefs != 0)
return false;
Builder.setInstr(MI);
const unsigned NumRegs = NumMergeRegs / NumDefs;
for (unsigned DefIdx = 0; DefIdx < NumDefs; ++DefIdx) {
SmallVector<Register, 8> Regs;
for (unsigned j = 0, Idx = NumRegs * DefIdx + 1; j < NumRegs;
++j, ++Idx)
Regs.push_back(MergeI->getOperand(Idx).getReg());
Register DefReg = MI.getReg(DefIdx);
Builder.buildMerge(DefReg, Regs);
UpdatedDefs.push_back(DefReg);
}
} else {
LLT MergeSrcTy = MRI.getType(MergeI->getOperand(1).getReg());
if (!ConvertOp && DestTy != MergeSrcTy)
ConvertOp = TargetOpcode::G_BITCAST;
if (ConvertOp) {
Builder.setInstr(MI);
for (unsigned Idx = 0; Idx < NumDefs; ++Idx) {
Register DefReg = MI.getOperand(Idx).getReg();
Register MergeSrc = MergeI->getOperand(Idx + 1).getReg();
if (!MRI.use_empty(DefReg)) {
Builder.buildInstr(ConvertOp, {DefReg}, {MergeSrc});
UpdatedDefs.push_back(DefReg);
}
}
markInstAndDefDead(MI, *MergeI, DeadInsts);
return true;
}
assert(DestTy == MergeSrcTy &&
"Bitcast and the other kinds of conversions should "
"have happened earlier");
Builder.setInstr(MI);
for (unsigned Idx = 0; Idx < NumDefs; ++Idx) {
Register DstReg = MI.getOperand(Idx).getReg();
Register SrcReg = MergeI->getOperand(Idx + 1).getReg();
replaceRegOrBuildCopy(DstReg, SrcReg, MRI, Builder, UpdatedDefs,
Observer);
}
}
markInstAndDefDead(MI, *MergeI, DeadInsts);
return true;
}
bool tryCombineExtract(MachineInstr &MI,
SmallVectorImpl<MachineInstr *> &DeadInsts,
SmallVectorImpl<Register> &UpdatedDefs) {
assert(MI.getOpcode() == TargetOpcode::G_EXTRACT);
Register SrcReg = lookThroughCopyInstrs(MI.getOperand(1).getReg());
MachineInstr *MergeI = MRI.getVRegDef(SrcReg);
if (!MergeI || !isa<GMergeLikeOp>(MergeI))
return false;
Register DstReg = MI.getOperand(0).getReg();
LLT DstTy = MRI.getType(DstReg);
LLT SrcTy = MRI.getType(SrcReg);
unsigned ExtractDstSize = DstTy.getSizeInBits();
unsigned Offset = MI.getOperand(2).getImm();
unsigned NumMergeSrcs = MergeI->getNumOperands() - 1;
unsigned MergeSrcSize = SrcTy.getSizeInBits() / NumMergeSrcs;
unsigned MergeSrcIdx = Offset / MergeSrcSize;
unsigned EndMergeSrcIdx = (Offset + ExtractDstSize - 1) / MergeSrcSize;
if (MergeSrcIdx != EndMergeSrcIdx)
return false;
Builder.setInstr(MI);
Builder.buildExtract(DstReg, MergeI->getOperand(MergeSrcIdx + 1).getReg(),
Offset - MergeSrcIdx * MergeSrcSize);
UpdatedDefs.push_back(DstReg);
markInstAndDefDead(MI, *MergeI, DeadInsts);
return true;
}
bool tryCombineInstruction(MachineInstr &MI,
SmallVectorImpl<MachineInstr *> &DeadInsts,
GISelObserverWrapper &WrapperObserver) {
if (!DeadInsts.empty())
deleteMarkedDeadInsts(DeadInsts, WrapperObserver);
SmallVector<Register, 4> UpdatedDefs;
bool Changed = false;
switch (MI.getOpcode()) {
default:
return false;
case TargetOpcode::G_ANYEXT:
Changed = tryCombineAnyExt(MI, DeadInsts, UpdatedDefs, WrapperObserver);
break;
case TargetOpcode::G_ZEXT:
Changed = tryCombineZExt(MI, DeadInsts, UpdatedDefs, WrapperObserver);
break;
case TargetOpcode::G_SEXT:
Changed = tryCombineSExt(MI, DeadInsts, UpdatedDefs);
break;
case TargetOpcode::G_UNMERGE_VALUES:
Changed = tryCombineUnmergeValues(cast<GUnmerge>(MI), DeadInsts,
UpdatedDefs, WrapperObserver);
break;
case TargetOpcode::G_MERGE_VALUES:
case TargetOpcode::G_BUILD_VECTOR:
case TargetOpcode::G_CONCAT_VECTORS:
for (MachineInstr &U : MRI.use_instructions(MI.getOperand(0).getReg())) {
if (U.getOpcode() == TargetOpcode::G_UNMERGE_VALUES ||
U.getOpcode() == TargetOpcode::G_TRUNC) {
UpdatedDefs.push_back(MI.getOperand(0).getReg());
break;
}
}
break;
case TargetOpcode::G_EXTRACT:
Changed = tryCombineExtract(MI, DeadInsts, UpdatedDefs);
break;
case TargetOpcode::G_TRUNC:
Changed = tryCombineTrunc(MI, DeadInsts, UpdatedDefs, WrapperObserver);
if (!Changed) {
UpdatedDefs.push_back(MI.getOperand(0).getReg());
}
break;
}
while (!UpdatedDefs.empty()) {
Register NewDef = UpdatedDefs.pop_back_val();
assert(NewDef.isVirtual() && "Unexpected redefinition of a physreg");
for (MachineInstr &Use : MRI.use_instructions(NewDef)) {
switch (Use.getOpcode()) {
case TargetOpcode::G_ANYEXT:
case TargetOpcode::G_ZEXT:
case TargetOpcode::G_SEXT:
case TargetOpcode::G_UNMERGE_VALUES:
case TargetOpcode::G_EXTRACT:
case TargetOpcode::G_TRUNC:
WrapperObserver.changedInstr(Use);
break;
case TargetOpcode::COPY: {
Register Copy = Use.getOperand(0).getReg();
if (Copy.isVirtual())
UpdatedDefs.push_back(Copy);
break;
}
default:
break;
}
}
}
return Changed;
}
private:
static Register getArtifactSrcReg(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case TargetOpcode::COPY:
case TargetOpcode::G_TRUNC:
case TargetOpcode::G_ZEXT:
case TargetOpcode::G_ANYEXT:
case TargetOpcode::G_SEXT:
case TargetOpcode::G_EXTRACT:
return MI.getOperand(1).getReg();
case TargetOpcode::G_UNMERGE_VALUES:
return MI.getOperand(MI.getNumOperands() - 1).getReg();
default:
llvm_unreachable("Not a legalization artifact happen");
}
}
void markDefDead(MachineInstr &MI, MachineInstr &DefMI,
SmallVectorImpl<MachineInstr *> &DeadInsts,
unsigned DefIdx = 0) {
MachineInstr *PrevMI = &MI;
while (PrevMI != &DefMI) {
Register PrevRegSrc = getArtifactSrcReg(*PrevMI);
MachineInstr *TmpDef = MRI.getVRegDef(PrevRegSrc);
if (MRI.hasOneUse(PrevRegSrc)) {
if (TmpDef != &DefMI) {
assert((TmpDef->getOpcode() == TargetOpcode::COPY ||
isArtifactCast(TmpDef->getOpcode())) &&
"Expecting copy or artifact cast here");
DeadInsts.push_back(TmpDef);
}
} else
break;
PrevMI = TmpDef;
}
if (PrevMI == &DefMI) {
unsigned I = 0;
bool IsDead = true;
for (MachineOperand &Def : DefMI.defs()) {
if (I != DefIdx) {
if (!MRI.use_empty(Def.getReg())) {
IsDead = false;
break;
}
} else {
if (!MRI.hasOneUse(DefMI.getOperand(DefIdx).getReg()))
break;
}
++I;
}
if (IsDead)
DeadInsts.push_back(&DefMI);
}
}
void markInstAndDefDead(MachineInstr &MI, MachineInstr &DefMI,
SmallVectorImpl<MachineInstr *> &DeadInsts,
unsigned DefIdx = 0) {
DeadInsts.push_back(&MI);
markDefDead(MI, DefMI, DeadInsts, DefIdx);
}
void deleteMarkedDeadInsts(SmallVectorImpl<MachineInstr *> &DeadInsts,
GISelObserverWrapper &WrapperObserver) {
for (auto *DeadMI : DeadInsts) {
LLVM_DEBUG(dbgs() << *DeadMI << "Is dead, eagerly deleting\n");
WrapperObserver.erasingInstr(*DeadMI);
DeadMI->eraseFromParent();
}
DeadInsts.clear();
}
bool isInstUnsupported(const LegalityQuery &Query) const {
using namespace LegalizeActions;
auto Step = LI.getAction(Query);
return Step.Action == Unsupported || Step.Action == NotFound;
}
bool isInstLegal(const LegalityQuery &Query) const {
return LI.getAction(Query).Action == LegalizeActions::Legal;
}
bool isConstantUnsupported(LLT Ty) const {
if (!Ty.isVector())
return isInstUnsupported({TargetOpcode::G_CONSTANT, {Ty}});
LLT EltTy = Ty.getElementType();
return isInstUnsupported({TargetOpcode::G_CONSTANT, {EltTy}}) ||
isInstUnsupported({TargetOpcode::G_BUILD_VECTOR, {Ty, EltTy}});
}
Register lookThroughCopyInstrs(Register Reg) {
using namespace llvm::MIPatternMatch;
Register TmpReg;
while (mi_match(Reg, MRI, m_Copy(m_Reg(TmpReg)))) {
if (MRI.getType(TmpReg).isValid())
Reg = TmpReg;
else
break;
}
return Reg;
}
};
}
#endif