| //===- CombinerHelperVectorOps.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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file implements CombinerHelper for G_EXTRACT_VECTOR_ELT, | |
| // G_INSERT_VECTOR_ELT, and G_VSCALE | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/CodeGen/GlobalISel/CombinerHelper.h" | |
| #include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h" | |
| #include "llvm/CodeGen/GlobalISel/LegalizerHelper.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/LowLevelTypeUtils.h" | |
| #include "llvm/CodeGen/MachineOperand.h" | |
| #include "llvm/CodeGen/MachineRegisterInfo.h" | |
| #include "llvm/CodeGen/TargetLowering.h" | |
| #include "llvm/CodeGen/TargetOpcodes.h" | |
| #include "llvm/Support/Casting.h" | |
| #include <optional> | |
| #define DEBUG_TYPE "gi-combiner" | |
| using namespace llvm; | |
| using namespace MIPatternMatch; | |
| bool CombinerHelper::matchExtractVectorElement(MachineInstr &MI, | |
| BuildFnTy &MatchInfo) const { | |
| GExtractVectorElement *Extract = cast<GExtractVectorElement>(&MI); | |
| Register Dst = Extract->getReg(0); | |
| Register Vector = Extract->getVectorReg(); | |
| Register Index = Extract->getIndexReg(); | |
| LLT DstTy = MRI.getType(Dst); | |
| LLT VectorTy = MRI.getType(Vector); | |
| // The vector register can be def'd by various ops that have vector as its | |
| // type. They can all be used for constant folding, scalarizing, | |
| // canonicalization, or combining based on symmetry. | |
| // | |
| // vector like ops | |
| // * build vector | |
| // * build vector trunc | |
| // * shuffle vector | |
| // * splat vector | |
| // * concat vectors | |
| // * insert/extract vector element | |
| // * insert/extract subvector | |
| // * vector loads | |
| // * scalable vector loads | |
| // | |
| // compute like ops | |
| // * binary ops | |
| // * unary ops | |
| // * exts and truncs | |
| // * casts | |
| // * fneg | |
| // * select | |
| // * phis | |
| // * cmps | |
| // * freeze | |
| // * bitcast | |
| // * undef | |
| // We try to get the value of the Index register. | |
| std::optional<ValueAndVReg> MaybeIndex = | |
| getIConstantVRegValWithLookThrough(Index, MRI); | |
| std::optional<APInt> IndexC = std::nullopt; | |
| if (MaybeIndex) | |
| IndexC = MaybeIndex->Value; | |
| // Fold extractVectorElement(Vector, TOOLARGE) -> undef | |
| if (IndexC && VectorTy.isFixedVector() && | |
| IndexC->uge(VectorTy.getNumElements()) && | |
| isLegalOrBeforeLegalizer({TargetOpcode::G_IMPLICIT_DEF, {DstTy}})) { | |
| // For fixed-length vectors, it's invalid to extract out-of-range elements. | |
| MatchInfo = [=](MachineIRBuilder &B) { B.buildUndef(Dst); }; | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool CombinerHelper::matchExtractVectorElementWithDifferentIndices( | |
| const MachineOperand &MO, BuildFnTy &MatchInfo) const { | |
| MachineInstr *Root = getDefIgnoringCopies(MO.getReg(), MRI); | |
| GExtractVectorElement *Extract = cast<GExtractVectorElement>(Root); | |
| // | |
| // %idx1:_(s64) = G_CONSTANT i64 1 | |
| // %idx2:_(s64) = G_CONSTANT i64 2 | |
| // %insert:_(<2 x s32>) = G_INSERT_VECTOR_ELT_ELT %bv(<2 x s32>), | |
| // %value(s32), %idx2(s64) %extract:_(s32) = G_EXTRACT_VECTOR_ELT %insert(<2 | |
| // x s32>), %idx1(s64) | |
| // | |
| // --> | |
| // | |
| // %insert:_(<2 x s32>) = G_INSERT_VECTOR_ELT_ELT %bv(<2 x s32>), | |
| // %value(s32), %idx2(s64) %extract:_(s32) = G_EXTRACT_VECTOR_ELT %bv(<2 x | |
| // s32>), %idx1(s64) | |
| // | |
| // | |
| Register Index = Extract->getIndexReg(); | |
| // We try to get the value of the Index register. | |
| std::optional<ValueAndVReg> MaybeIndex = | |
| getIConstantVRegValWithLookThrough(Index, MRI); | |
| std::optional<APInt> IndexC = std::nullopt; | |
| if (!MaybeIndex) | |
| return false; | |
| else | |
| IndexC = MaybeIndex->Value; | |
| Register Vector = Extract->getVectorReg(); | |
| GInsertVectorElement *Insert = | |
| getOpcodeDef<GInsertVectorElement>(Vector, MRI); | |
| if (!Insert) | |
| return false; | |
| Register Dst = Extract->getReg(0); | |
| std::optional<ValueAndVReg> MaybeInsertIndex = | |
| getIConstantVRegValWithLookThrough(Insert->getIndexReg(), MRI); | |
| if (MaybeInsertIndex && MaybeInsertIndex->Value != *IndexC) { | |
| // There is no one-use check. We have to keep the insert. When both Index | |
| // registers are constants and not equal, we can look into the Vector | |
| // register of the insert. | |
| MatchInfo = [=](MachineIRBuilder &B) { | |
| B.buildExtractVectorElement(Dst, Insert->getVectorReg(), Index); | |
| }; | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool CombinerHelper::matchExtractVectorElementWithBuildVector( | |
| const MachineInstr &MI, const MachineInstr &MI2, | |
| BuildFnTy &MatchInfo) const { | |
| const GExtractVectorElement *Extract = cast<GExtractVectorElement>(&MI); | |
| const GBuildVector *Build = cast<GBuildVector>(&MI2); | |
| // | |
| // %zero:_(s64) = G_CONSTANT i64 0 | |
| // %bv:_(<2 x s32>) = G_BUILD_VECTOR %arg1(s32), %arg2(s32) | |
| // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %bv(<2 x s32>), %zero(s64) | |
| // | |
| // --> | |
| // | |
| // %extract:_(32) = COPY %arg1(s32) | |
| // | |
| // | |
| Register Vector = Extract->getVectorReg(); | |
| LLT VectorTy = MRI.getType(Vector); | |
| // There is a one-use check. There are more combines on build vectors. | |
| EVT Ty(getMVTForLLT(VectorTy)); | |
| if (!MRI.hasOneNonDBGUse(Build->getReg(0)) || | |
| !getTargetLowering().aggressivelyPreferBuildVectorSources(Ty)) | |
| return false; | |
| APInt Index = getIConstantFromReg(Extract->getIndexReg(), MRI); | |
| // We now know that there is a buildVector def'd on the Vector register and | |
| // the index is const. The combine will succeed. | |
| Register Dst = Extract->getReg(0); | |
| MatchInfo = [=](MachineIRBuilder &B) { | |
| B.buildCopy(Dst, Build->getSourceReg(Index.getZExtValue())); | |
| }; | |
| return true; | |
| } | |
| bool CombinerHelper::matchExtractVectorElementWithBuildVectorTrunc( | |
| const MachineOperand &MO, BuildFnTy &MatchInfo) const { | |
| MachineInstr *Root = getDefIgnoringCopies(MO.getReg(), MRI); | |
| GExtractVectorElement *Extract = cast<GExtractVectorElement>(Root); | |
| // | |
| // %zero:_(s64) = G_CONSTANT i64 0 | |
| // %bv:_(<2 x s32>) = G_BUILD_VECTOR_TRUNC %arg1(s64), %arg2(s64) | |
| // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %bv(<2 x s32>), %zero(s64) | |
| // | |
| // --> | |
| // | |
| // %extract:_(32) = G_TRUNC %arg1(s64) | |
| // | |
| // | |
| // | |
| // %bv:_(<2 x s32>) = G_BUILD_VECTOR_TRUNC %arg1(s64), %arg2(s64) | |
| // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %bv(<2 x s32>), %opaque(s64) | |
| // | |
| // --> | |
| // | |
| // %bv:_(<2 x s32>) = G_BUILD_VECTOR_TRUNC %arg1(s64), %arg2(s64) | |
| // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %bv(<2 x s32>), %opaque(s64) | |
| // | |
| Register Vector = Extract->getVectorReg(); | |
| // We expect a buildVectorTrunc on the Vector register. | |
| GBuildVectorTrunc *Build = getOpcodeDef<GBuildVectorTrunc>(Vector, MRI); | |
| if (!Build) | |
| return false; | |
| LLT VectorTy = MRI.getType(Vector); | |
| // There is a one-use check. There are more combines on build vectors. | |
| EVT Ty(getMVTForLLT(VectorTy)); | |
| if (!MRI.hasOneNonDBGUse(Build->getReg(0)) || | |
| !getTargetLowering().aggressivelyPreferBuildVectorSources(Ty)) | |
| return false; | |
| Register Index = Extract->getIndexReg(); | |
| // If the Index is constant, then we can extract the element from the given | |
| // offset. | |
| std::optional<ValueAndVReg> MaybeIndex = | |
| getIConstantVRegValWithLookThrough(Index, MRI); | |
| if (!MaybeIndex) | |
| return false; | |
| // We now know that there is a buildVectorTrunc def'd on the Vector register | |
| // and the index is const. The combine will succeed. | |
| Register Dst = Extract->getReg(0); | |
| LLT DstTy = MRI.getType(Dst); | |
| LLT SrcTy = MRI.getType(Build->getSourceReg(0)); | |
| // For buildVectorTrunc, the inputs are truncated. | |
| if (!isLegalOrBeforeLegalizer({TargetOpcode::G_TRUNC, {DstTy, SrcTy}})) | |
| return false; | |
| MatchInfo = [=](MachineIRBuilder &B) { | |
| B.buildTrunc(Dst, Build->getSourceReg(MaybeIndex->Value.getZExtValue())); | |
| }; | |
| return true; | |
| } | |
| bool CombinerHelper::matchExtractVectorElementWithShuffleVector( | |
| const MachineInstr &MI, const MachineInstr &MI2, | |
| BuildFnTy &MatchInfo) const { | |
| const GExtractVectorElement *Extract = cast<GExtractVectorElement>(&MI); | |
| const GShuffleVector *Shuffle = cast<GShuffleVector>(&MI2); | |
| // | |
| // %zero:_(s64) = G_CONSTANT i64 0 | |
| // %sv:_(<4 x s32>) = G_SHUFFLE_SHUFFLE %arg1(<4 x s32>), %arg2(<4 x s32>), | |
| // shufflemask(0, 0, 0, 0) | |
| // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %sv(<4 x s32>), %zero(s64) | |
| // | |
| // --> | |
| // | |
| // %zero1:_(s64) = G_CONSTANT i64 0 | |
| // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %arg1(<4 x s32>), %zero1(s64) | |
| // | |
| // | |
| // | |
| // | |
| // %three:_(s64) = G_CONSTANT i64 3 | |
| // %sv:_(<4 x s32>) = G_SHUFFLE_SHUFFLE %arg1(<4 x s32>), %arg2(<4 x s32>), | |
| // shufflemask(0, 0, 0, -1) | |
| // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %sv(<4 x s32>), %three(s64) | |
| // | |
| // --> | |
| // | |
| // %extract:_(s32) = G_IMPLICIT_DEF | |
| // | |
| // | |
| APInt Index = getIConstantFromReg(Extract->getIndexReg(), MRI); | |
| ArrayRef<int> Mask = Shuffle->getMask(); | |
| unsigned Offset = Index.getZExtValue(); | |
| int SrcIdx = Mask[Offset]; | |
| LLT Src1Type = MRI.getType(Shuffle->getSrc1Reg()); | |
| // At the IR level a <1 x ty> shuffle vector is valid, but we want to extract | |
| // from a vector. | |
| assert(Src1Type.isVector() && "expected to extract from a vector"); | |
| unsigned LHSWidth = Src1Type.isVector() ? Src1Type.getNumElements() : 1; | |
| // Note that there is no one use check. | |
| Register Dst = Extract->getReg(0); | |
| LLT DstTy = MRI.getType(Dst); | |
| if (SrcIdx < 0 && | |
| isLegalOrBeforeLegalizer({TargetOpcode::G_IMPLICIT_DEF, {DstTy}})) { | |
| MatchInfo = [=](MachineIRBuilder &B) { B.buildUndef(Dst); }; | |
| return true; | |
| } | |
| // If the legality check failed, then we still have to abort. | |
| if (SrcIdx < 0) | |
| return false; | |
| Register NewVector; | |
| // We check in which vector and at what offset to look through. | |
| if (SrcIdx < (int)LHSWidth) { | |
| NewVector = Shuffle->getSrc1Reg(); | |
| // SrcIdx unchanged | |
| } else { // SrcIdx >= LHSWidth | |
| NewVector = Shuffle->getSrc2Reg(); | |
| SrcIdx -= LHSWidth; | |
| } | |
| LLT IdxTy = MRI.getType(Extract->getIndexReg()); | |
| LLT NewVectorTy = MRI.getType(NewVector); | |
| // We check the legality of the look through. | |
| if (!isLegalOrBeforeLegalizer( | |
| {TargetOpcode::G_EXTRACT_VECTOR_ELT, {DstTy, NewVectorTy, IdxTy}}) || | |
| !isConstantLegalOrBeforeLegalizer({IdxTy})) | |
| return false; | |
| // We look through the shuffle vector. | |
| MatchInfo = [=](MachineIRBuilder &B) { | |
| auto Idx = B.buildConstant(IdxTy, SrcIdx); | |
| B.buildExtractVectorElement(Dst, NewVector, Idx); | |
| }; | |
| return true; | |
| } | |
| bool CombinerHelper::matchInsertVectorElementOOB(MachineInstr &MI, | |
| BuildFnTy &MatchInfo) const { | |
| GInsertVectorElement *Insert = cast<GInsertVectorElement>(&MI); | |
| Register Dst = Insert->getReg(0); | |
| LLT DstTy = MRI.getType(Dst); | |
| Register Index = Insert->getIndexReg(); | |
| if (!DstTy.isFixedVector()) | |
| return false; | |
| std::optional<ValueAndVReg> MaybeIndex = | |
| getIConstantVRegValWithLookThrough(Index, MRI); | |
| if (MaybeIndex && MaybeIndex->Value.uge(DstTy.getNumElements()) && | |
| isLegalOrBeforeLegalizer({TargetOpcode::G_IMPLICIT_DEF, {DstTy}})) { | |
| MatchInfo = [=](MachineIRBuilder &B) { B.buildUndef(Dst); }; | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool CombinerHelper::matchAddOfVScale(const MachineOperand &MO, | |
| BuildFnTy &MatchInfo) const { | |
| Register Dst = MO.getReg(); | |
| GVScale *LHSVScale, *RHSVScale; | |
| if (!mi_match(Dst, MRI, m_GAdd(m_GVScale(LHSVScale), m_GVScale(RHSVScale)))) | |
| return false; | |
| if (!MRI.hasOneNonDBGUse(LHSVScale->getReg(0)) || | |
| !MRI.hasOneNonDBGUse(RHSVScale->getReg(0))) | |
| return false; | |
| MatchInfo = [=](MachineIRBuilder &B) { | |
| B.buildVScale(Dst, LHSVScale->getSrc() + RHSVScale->getSrc()); | |
| }; | |
| return true; | |
| } | |
| bool CombinerHelper::matchMulOfVScale(const MachineOperand &MO, | |
| BuildFnTy &MatchInfo) const { | |
| Register Dst = MO.getReg(); | |
| GVScale *LHSVScale; | |
| Register RHSReg; | |
| if (!mi_match(Dst, MRI, m_GMul(m_GVScale(LHSVScale), m_Reg(RHSReg)))) | |
| return false; | |
| std::optional<APInt> MaybeRHS = getIConstantVRegVal(RHSReg, MRI); | |
| if (!MaybeRHS) | |
| return false; | |
| if (!MRI.hasOneNonDBGUse(LHSVScale->getReg(0))) | |
| return false; | |
| MatchInfo = [=](MachineIRBuilder &B) { | |
| B.buildVScale(Dst, LHSVScale->getSrc() * *MaybeRHS); | |
| }; | |
| return true; | |
| } | |
| bool CombinerHelper::matchSubOfVScale(const MachineOperand &MO, | |
| BuildFnTy &MatchInfo) const { | |
| Register Dst = MO.getReg(); | |
| Register SubLHS; | |
| GVScale *RHSVScale; | |
| uint32_t Flags; | |
| if (!mi_match(Dst, MRI, | |
| m_GSub(m_Reg(SubLHS), m_GVScale(RHSVScale), m_MIFlags(Flags)))) | |
| return false; | |
| LLT DstTy = MRI.getType(Dst); | |
| if (!MRI.hasOneNonDBGUse(RHSVScale->getReg(0)) || | |
| !isLegalOrBeforeLegalizer({TargetOpcode::G_ADD, DstTy})) | |
| return false; | |
| MatchInfo = [=](MachineIRBuilder &B) { | |
| auto VScale = B.buildVScale(DstTy, -RHSVScale->getSrc()); | |
| B.buildAdd(Dst, SubLHS, VScale, Flags); | |
| }; | |
| return true; | |
| } | |
| bool CombinerHelper::matchShlOfVScale(const MachineOperand &MO, | |
| BuildFnTy &MatchInfo) const { | |
| Register Dst = MO.getReg(); | |
| GVScale *LHSVScale; | |
| Register ShiftReg; | |
| if (!mi_match(Dst, MRI, m_GShl(m_GVScale(LHSVScale), m_Reg(ShiftReg)))) | |
| return false; | |
| std::optional<APInt> MaybeRHS = getIConstantVRegVal(ShiftReg, MRI); | |
| if (!MaybeRHS) | |
| return false; | |
| LLT DstTy = MRI.getType(Dst); | |
| if (!MRI.hasOneNonDBGUse(LHSVScale->getReg(0)) || | |
| !isLegalOrBeforeLegalizer({TargetOpcode::G_VSCALE, DstTy})) | |
| return false; | |
| MatchInfo = [=](MachineIRBuilder &B) { | |
| B.buildVScale(Dst, LHSVScale->getSrc().shl(*MaybeRHS)); | |
| }; | |
| return true; | |
| } |