//===-- llvm/CodeGen/GlobalISel/LegalizerCombiner.h --===========// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // This file contains some helper functions which try to cleanup artifacts // such as G_TRUNCs/G_[ZSA]EXTENDS that were created during legalization to make // the types match. This file also contains some combines of merges that happens // at the end of the legalization. //===----------------------------------------------------------------------===// #include "llvm/CodeGen/GlobalISel/Legalizer.h" #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" #include "llvm/CodeGen/GlobalISel/Utils.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/Support/Debug.h" #define DEBUG_TYPE "legalizer" namespace llvm { class LegalizerCombiner { MachineIRBuilder &Builder; MachineRegisterInfo &MRI; public: LegalizerCombiner(MachineIRBuilder &B, MachineRegisterInfo &MRI) : Builder(B), MRI(MRI) {} bool tryCombineAnyExt(MachineInstr &MI, SmallVectorImpl<MachineInstr *> &DeadInsts) { if (MI.getOpcode() != TargetOpcode::G_ANYEXT) return false; MachineInstr *DefMI = MRI.getVRegDef(MI.getOperand(1).getReg()); if (DefMI->getOpcode() == TargetOpcode::G_TRUNC) { DEBUG(dbgs() << ".. Combine MI: " << MI;); unsigned DstReg = MI.getOperand(0).getReg(); unsigned SrcReg = DefMI->getOperand(1).getReg(); Builder.setInstr(MI); // We get a copy/trunc/extend depending on the sizes Builder.buildAnyExtOrTrunc(DstReg, SrcReg); markInstAndDefDead(MI, *DefMI, DeadInsts); return true; } return false; } bool tryCombineZExt(MachineInstr &MI, SmallVectorImpl<MachineInstr *> &DeadInsts) { if (MI.getOpcode() != TargetOpcode::G_ZEXT) return false; MachineInstr *DefMI = MRI.getVRegDef(MI.getOperand(1).getReg()); if (DefMI->getOpcode() == TargetOpcode::G_TRUNC) { DEBUG(dbgs() << ".. Combine MI: " << MI;); Builder.setInstr(MI); unsigned DstReg = MI.getOperand(0).getReg(); unsigned ZExtSrc = MI.getOperand(1).getReg(); LLT ZExtSrcTy = MRI.getType(ZExtSrc); LLT DstTy = MRI.getType(DstReg); APInt Mask = APInt::getAllOnesValue(ZExtSrcTy.getSizeInBits()); auto MaskCstMIB = Builder.buildConstant(DstTy, Mask.getZExtValue()); unsigned TruncSrc = DefMI->getOperand(1).getReg(); // We get a copy/trunc/extend depending on the sizes auto SrcCopyOrTrunc = Builder.buildAnyExtOrTrunc(DstTy, TruncSrc); Builder.buildAnd(DstReg, SrcCopyOrTrunc, MaskCstMIB); markInstAndDefDead(MI, *DefMI, DeadInsts); return true; } return false; } bool tryCombineSExt(MachineInstr &MI, SmallVectorImpl<MachineInstr *> &DeadInsts) { if (MI.getOpcode() != TargetOpcode::G_SEXT) return false; MachineInstr *DefMI = MRI.getVRegDef(MI.getOperand(1).getReg()); if (DefMI->getOpcode() == TargetOpcode::G_TRUNC) { DEBUG(dbgs() << ".. Combine MI: " << MI;); Builder.setInstr(MI); unsigned DstReg = MI.getOperand(0).getReg(); LLT DstTy = MRI.getType(DstReg); unsigned SExtSrc = MI.getOperand(1).getReg(); LLT SExtSrcTy = MRI.getType(SExtSrc); unsigned SizeDiff = DstTy.getSizeInBits() - SExtSrcTy.getSizeInBits(); auto SizeDiffMIB = Builder.buildConstant(DstTy, SizeDiff); unsigned TruncSrcReg = DefMI->getOperand(1).getReg(); // We get a copy/trunc/extend depending on the sizes auto SrcCopyExtOrTrunc = Builder.buildAnyExtOrTrunc(DstTy, TruncSrcReg); auto ShlMIB = Builder.buildInstr(TargetOpcode::G_SHL, DstTy, SrcCopyExtOrTrunc, SizeDiffMIB); Builder.buildInstr(TargetOpcode::G_ASHR, DstReg, ShlMIB, SizeDiffMIB); markInstAndDefDead(MI, *DefMI, DeadInsts); return true; } return false; } bool tryCombineMerges(MachineInstr &MI, SmallVectorImpl<MachineInstr *> &DeadInsts) { if (MI.getOpcode() != TargetOpcode::G_UNMERGE_VALUES) return false; unsigned NumDefs = MI.getNumOperands() - 1; unsigned SrcReg = MI.getOperand(NumDefs).getReg(); MachineInstr *MergeI = MRI.getVRegDef(SrcReg); if (!MergeI || (MergeI->getOpcode() != TargetOpcode::G_MERGE_VALUES)) return false; const unsigned NumMergeRegs = MergeI->getNumOperands() - 1; if (NumMergeRegs < NumDefs) { if (NumDefs % NumMergeRegs != 0) return false; Builder.setInstr(MI); // Transform to UNMERGEs, for example // %1 = G_MERGE_VALUES %4, %5 // %9, %10, %11, %12 = G_UNMERGE_VALUES %1 // to // %9, %10 = G_UNMERGE_VALUES %4 // %11, %12 = G_UNMERGE_VALUES %5 const unsigned NewNumDefs = NumDefs / NumMergeRegs; for (unsigned Idx = 0; Idx < NumMergeRegs; ++Idx) { SmallVector<unsigned, 2> DstRegs; for (unsigned j = 0, DefIdx = Idx * NewNumDefs; j < NewNumDefs; ++j, ++DefIdx) DstRegs.push_back(MI.getOperand(DefIdx).getReg()); Builder.buildUnmerge(DstRegs, MergeI->getOperand(Idx + 1).getReg()); } } else if (NumMergeRegs > NumDefs) { if (NumMergeRegs % NumDefs != 0) return false; Builder.setInstr(MI); // Transform to MERGEs // %6 = G_MERGE_VALUES %17, %18, %19, %20 // %7, %8 = G_UNMERGE_VALUES %6 // to // %7 = G_MERGE_VALUES %17, %18 // %8 = G_MERGE_VALUES %19, %20 const unsigned NumRegs = NumMergeRegs / NumDefs; for (unsigned DefIdx = 0; DefIdx < NumDefs; ++DefIdx) { SmallVector<unsigned, 2> Regs; for (unsigned j = 0, Idx = NumRegs * DefIdx + 1; j < NumRegs; ++j, ++Idx) Regs.push_back(MergeI->getOperand(Idx).getReg()); Builder.buildMerge(MI.getOperand(DefIdx).getReg(), Regs); } } else { // FIXME: is a COPY appropriate if the types mismatch? We know both // registers are allocatable by now. if (MRI.getType(MI.getOperand(0).getReg()) != MRI.getType(MergeI->getOperand(1).getReg())) return false; for (unsigned Idx = 0; Idx < NumDefs; ++Idx) MRI.replaceRegWith(MI.getOperand(Idx).getReg(), MergeI->getOperand(Idx + 1).getReg()); } markInstAndDefDead(MI, *MergeI, DeadInsts); return true; } /// Try to combine away MI. /// Returns true if it combined away the MI. /// Adds instructions that are dead as a result of the combine /// into DeadInsts, which can include MI. bool tryCombineInstruction(MachineInstr &MI, SmallVectorImpl<MachineInstr *> &DeadInsts) { switch (MI.getOpcode()) { default: return false; case TargetOpcode::G_ANYEXT: return tryCombineAnyExt(MI, DeadInsts); case TargetOpcode::G_ZEXT: return tryCombineZExt(MI, DeadInsts); case TargetOpcode::G_SEXT: return tryCombineSExt(MI, DeadInsts); case TargetOpcode::G_UNMERGE_VALUES: return tryCombineMerges(MI, DeadInsts); } } private: /// Mark MI as dead. If a def of one of MI's operands, DefMI, would also be /// dead due to MI being killed, then mark DefMI as dead too. void markInstAndDefDead(MachineInstr &MI, MachineInstr &DefMI, SmallVectorImpl<MachineInstr *> &DeadInsts) { DeadInsts.push_back(&MI); if (MRI.hasOneUse(DefMI.getOperand(0).getReg())) DeadInsts.push_back(&DefMI); } }; } // namespace llvm