Index: llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h =================================================================== --- llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h +++ llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h @@ -27,6 +27,7 @@ class MachineRegisterInfo; class MachineInstr; class MachineOperand; +class GISelKnownBits; struct PreferredTuple { LLT Ty; // The result type of the extend. @@ -38,9 +39,11 @@ MachineIRBuilder &Builder; MachineRegisterInfo &MRI; GISelChangeObserver &Observer; + GISelKnownBits *KB; public: - CombinerHelper(GISelChangeObserver &Observer, MachineIRBuilder &B); + CombinerHelper(GISelChangeObserver &Observer, MachineIRBuilder &B, + GISelKnownBits *KB = nullptr); /// MachineRegisterInfo::replaceRegWith() and inform the observer of the changes void replaceRegWith(MachineRegisterInfo &MRI, Register FromReg, Register ToReg) const; Index: llvm/include/llvm/CodeGen/GlobalISel/GISelKnownBits.h =================================================================== --- /dev/null +++ llvm/include/llvm/CodeGen/GlobalISel/GISelKnownBits.h @@ -0,0 +1,106 @@ +//===- llvm/CodeGen/GlobalISel/KnownBitsInfo.h ------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +/// Provides analysis for querying information about KnownBits during GISel +/// passes. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CODEGEN_GLOBALISEL_KNOWNBITSINFO_H +#define LLVM_CODEGEN_GLOBALISEL_KNOWNBITSINFO_H + +#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/Register.h" +#include "llvm/IR/PassManager.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/KnownBits.h" + +namespace llvm { + +class TargetLowering; +class DataLayout; + +class GISelKnownBits : public GISelChangeObserver { + MachineRegisterInfo *MRI = nullptr; + MachineFunction *MF = nullptr; + const TargetLowering *TL = nullptr; + const DataLayout *DL = nullptr; + +public: + GISelKnownBits() = default; + virtual ~GISelKnownBits() = default; + void setMF(MachineFunction &MF); + virtual void computeKnownBitsImpl(Register R, KnownBits &Known, + const APInt &DemandedElts, + unsigned Depth = 0); + + // KnownBitsAPI + KnownBits getKnownBits(Register R); + // Calls getKnownBits for first operand def of MI. + KnownBits getKnownBits(MachineInstr &MI); + APInt getKnownZeroes(Register R); + APInt getKnownOnes(Register R); + + // FIXME: Is this the right place for G_FRAME_INDEX? Should it be in + // TargetLowering? + void computeKnownBitsForFrameIndex(Register R, KnownBits &Known, + const APInt &DemandedElts, + unsigned Depth = 0); + static unsigned inferAlignmentForFrameIdx(int FrameIdx, int Offset, + const MachineFunction &MF); + static void computeKnownBitsForAlignment(KnownBits &Known, unsigned Align); + + // Try to infer alignment for MI. + static unsigned inferPtrAlignment(const MachineInstr &MI); + + // Manually set KnownBits for a register. + // Only used if caching/serializing to MIR etc. + void setKnownBits(Register R, const KnownBits &Known); + + // Observer API. No-op for non-caching implementation. + void erasingInstr(MachineInstr &MI) override{}; + void createdInstr(MachineInstr &MI) override{}; + void changingInstr(MachineInstr &MI) override{}; + void changedInstr(MachineInstr &MI) override{}; + + void clear() { + MF = nullptr; + MRI = nullptr; + DL = nullptr; + TL = nullptr; + } + + constexpr unsigned getMaxDepth() const { return 6; } +}; + +/// To use KnownBitsInfo analysis in a pass, +/// KnownBitsInfo &Info = getAnalysis().get(); +/// Add to observer if the Info is caching. +/// WrapperObserver.addObserver(Info); + +/// Eventually add other features such as caching/ser/deserializing +/// to MIR etc. Those implementations can derive from KnownBitsInfoBase +/// and override computeKnownBitsImpl. +class GISelKnownBitsAnalysis : public MachineFunctionPass { + GISelKnownBits Info; + +public: + static char ID; + GISelKnownBitsAnalysis() : MachineFunctionPass(ID) { + initializeGISelKnownBitsAnalysisPass(*PassRegistry::getPassRegistry()); + } + GISelKnownBits &get() { return Info; } + const GISelKnownBits &get() const { return Info; } + void getAnalysisUsage(AnalysisUsage &AU) const override; + bool runOnMachineFunction(MachineFunction &MF) override; + void releaseMemory() override { Info.clear(); } +}; +} // namespace llvm + +#endif // ifdef Index: llvm/include/llvm/CodeGen/TargetLowering.h =================================================================== --- llvm/include/llvm/CodeGen/TargetLowering.h +++ llvm/include/llvm/CodeGen/TargetLowering.h @@ -3169,6 +3169,14 @@ const APInt &DemandedElts, const SelectionDAG &DAG, unsigned Depth = 0) const; + /// Determine which of the bits specified in Mask are known to be either zero + /// or one and return them in the KnownZero/KnownOne bitsets. The DemandedElts + /// argument allows us to only collect the known bits that are shared by the + /// requested vector elements. This is for GISel. + virtual void computeKnownBitsForTargetInstr(Register R, KnownBits &Known, + const APInt &DemandedElts, + const MachineRegisterInfo &MRI, + unsigned Depth = 0) const; /// Determine which of the bits of FrameIndex \p FIOp are known to be 0. /// Default implementation computes low bits based on alignment Index: llvm/include/llvm/InitializePasses.h =================================================================== --- llvm/include/llvm/InitializePasses.h +++ llvm/include/llvm/InitializePasses.h @@ -202,6 +202,7 @@ void initializeLegacyLoopSinkPassPass(PassRegistry&); void initializeLegalizerPass(PassRegistry&); void initializeGISelCSEAnalysisWrapperPassPass(PassRegistry &); +void initializeGISelKnownBitsAnalysisPass(PassRegistry &); void initializeLibCallsShrinkWrapLegacyPassPass(PassRegistry&); void initializeLintPass(PassRegistry&); void initializeLiveDebugValuesPass(PassRegistry&); Index: llvm/lib/CodeGen/GlobalISel/CMakeLists.txt =================================================================== --- llvm/lib/CodeGen/GlobalISel/CMakeLists.txt +++ llvm/lib/CodeGen/GlobalISel/CMakeLists.txt @@ -1,5 +1,6 @@ add_llvm_library(LLVMGlobalISel CSEInfo.cpp + GISelKnownBits.cpp CSEMIRBuilder.cpp CallLowering.cpp GlobalISel.cpp Index: llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp =================================================================== --- llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp +++ llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp @@ -8,6 +8,7 @@ #include "llvm/CodeGen/GlobalISel/CombinerHelper.h" #include "llvm/CodeGen/GlobalISel/Combiner.h" #include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h" +#include "llvm/CodeGen/GlobalISel/GISelKnownBits.h" #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" #include "llvm/CodeGen/GlobalISel/Utils.h" #include "llvm/CodeGen/MachineFrameInfo.h" @@ -22,8 +23,11 @@ using namespace llvm; CombinerHelper::CombinerHelper(GISelChangeObserver &Observer, - MachineIRBuilder &B) - : Builder(B), MRI(Builder.getMF().getRegInfo()), Observer(Observer) {} + MachineIRBuilder &B, GISelKnownBits *KB) + : Builder(B), MRI(Builder.getMF().getRegInfo()), Observer(Observer), + KB(KB) { + (void)this->KB; +} void CombinerHelper::replaceRegWith(MachineRegisterInfo &MRI, Register FromReg, Register ToReg) const { Index: llvm/lib/CodeGen/GlobalISel/GISelKnownBits.cpp =================================================================== --- /dev/null +++ llvm/lib/CodeGen/GlobalISel/GISelKnownBits.cpp @@ -0,0 +1,337 @@ +//===- lib/CodeGen/GlobalISel/KnownBitsInfo.cpp ------------------*- C++ +//-*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +/// Provides analysis for querying information about KnownBits during GISel +/// passes. +// +//===------------------ +#include "llvm/CodeGen/GlobalISel/GISelKnownBits.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/CodeGen/GlobalISel/Utils.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetLowering.h" +#include "llvm/CodeGen/TargetOpcodes.h" + +#define DEBUG_TYPE "gisel-known-bits" + +using namespace llvm; + +char llvm::GISelKnownBitsAnalysis::ID = 0; + +INITIALIZE_PASS_BEGIN(GISelKnownBitsAnalysis, DEBUG_TYPE, + "Analysis for ComputingKnownBits", false, true) +INITIALIZE_PASS_END(GISelKnownBitsAnalysis, DEBUG_TYPE, + "Analysis for ComputingKnownBits", false, true) + +void GISelKnownBits::setMF(MachineFunction &MF) { + this->MF = &MF; + this->MRI = &MF.getRegInfo(); + this->TL = MF.getSubtarget().getTargetLowering(); + const Function &F = MF.getFunction(); + this->DL = &F.getParent()->getDataLayout(); +} + +unsigned GISelKnownBits::inferAlignmentForFrameIdx(int FrameIdx, int Offset, + const MachineFunction &MF) { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + return MinAlign(Offset, MFI.getObjectAlignment(FrameIdx)); + // TODO: How to handle cases with Base + Offset? +} + +unsigned GISelKnownBits::inferPtrAlignment(const MachineInstr &MI) { + if (MI.getOpcode() == TargetOpcode::G_FRAME_INDEX) { + int FrameIdx = MI.getOperand(1).getIndex(); + return inferAlignmentForFrameIdx(FrameIdx, 0, *MI.getMF()); + } + return 0; +} + +void GISelKnownBits::computeKnownBitsForFrameIndex(Register R, KnownBits &Known, + const APInt &DemandedElts, + unsigned Depth) { + const MachineInstr &MI = *MRI->getVRegDef(R); + computeKnownBitsForAlignment(Known, inferPtrAlignment(MI)); +} + +void GISelKnownBits::computeKnownBitsForAlignment(KnownBits &Known, + unsigned Align) { + if (Align) + // The low bits are known zero if the pointer is aligned. + Known.Zero.setLowBits(Log2_32(Align)); +} + +KnownBits GISelKnownBits::getKnownBits(MachineInstr &MI) { + return getKnownBits(MI.getOperand(0).getReg()); +} + +KnownBits GISelKnownBits::getKnownBits(Register R) { + assert(MF && "MF Not set"); + KnownBits Known; + LLT Ty = MRI->getType(R); + APInt DemandedElts = + Ty.isVector() ? APInt::getAllOnesValue(Ty.getNumElements()) : APInt(1, 1); + computeKnownBitsImpl(R, Known, DemandedElts); + return Known; +} + +APInt GISelKnownBits::getKnownZeroes(Register R) { + return getKnownBits(R).Zero; +} + +APInt GISelKnownBits::getKnownOnes(Register R) { return getKnownBits(R).One; } + +void GISelKnownBits::computeKnownBitsImpl(Register R, KnownBits &Known, + const APInt &DemandedElts, + unsigned Depth) { + MachineInstr &MI = *MRI->getVRegDef(R); + unsigned Opcode = MI.getOpcode(); + LLT DstTy = MRI->getType(R); + + unsigned BitWidth = DstTy.getSizeInBits(); + Known = KnownBits(BitWidth); // Don't know anything + + if (DstTy.isVector()) + return; // TODO: Handle vectors. + + if (Depth == getMaxDepth()) + return; + + if (!DemandedElts) + return; // No demanded elts, better to assume we don't know anything. + + KnownBits Known2; + + switch (Opcode) { + default: + TL->computeKnownBitsForTargetInstr(R, Known, DemandedElts, *MRI, Depth); + break; + case TargetOpcode::G_CONSTANT: { + auto CstVal = getConstantVRegVal(R, *MRI); + Known.One = *CstVal; + Known.Zero = ~Known.One; + break; + } + case TargetOpcode::G_FRAME_INDEX: { + computeKnownBitsForFrameIndex(R, Known, DemandedElts); + break; + } + case TargetOpcode::G_SUB: { + // If low bits are known to be zero in both operands, then we know they are + // going to be 0 in the result. Both addition and complement operations + // preserve the low zero bits. + computeKnownBitsImpl(MI.getOperand(1).getReg(), Known2, DemandedElts, + Depth + 1); + unsigned KnownZeroLow = Known2.countMinTrailingZeros(); + if (KnownZeroLow == 0) + break; + computeKnownBitsImpl(MI.getOperand(2).getReg(), Known2, DemandedElts, + Depth + 1); + KnownZeroLow = std::min(KnownZeroLow, Known2.countMinTrailingZeros()); + Known.Zero.setLowBits(KnownZeroLow); + break; + } + // G_GEP is like G_ADD. FIXME: Is this true for all targets? + case TargetOpcode::G_GEP: + case TargetOpcode::G_ADD: { + // Output known-0 bits are known if clear or set in both the low clear bits + // common to both LHS & RHS. For example, 8+(X<<3) is known to have the + // low 3 bits clear. + // Output known-0 bits are also known if the top bits of each input are + // known to be clear. For example, if one input has the top 10 bits clear + // and the other has the top 8 bits clear, we know the top 7 bits of the + // output must be clear. + computeKnownBitsImpl(MI.getOperand(1).getReg(), Known2, DemandedElts, + Depth + 1); + unsigned KnownZeroHigh = Known2.countMinLeadingZeros(); + unsigned KnownZeroLow = Known2.countMinTrailingZeros(); + computeKnownBitsImpl(MI.getOperand(2).getReg(), Known2, DemandedElts, + Depth + 1); + KnownZeroHigh = std::min(KnownZeroHigh, Known2.countMinLeadingZeros()); + KnownZeroLow = std::min(KnownZeroLow, Known2.countMinTrailingZeros()); + Known.Zero.setLowBits(KnownZeroLow); + if (KnownZeroHigh > 1) + Known.Zero.setHighBits(KnownZeroHigh - 1); + break; + } + case TargetOpcode::G_AND: { + // If either the LHS or the RHS are Zero, the result is zero. + computeKnownBitsImpl(MI.getOperand(2).getReg(), Known, DemandedElts, + Depth + 1); + computeKnownBitsImpl(MI.getOperand(1).getReg(), Known2, DemandedElts, + Depth + 1); + + // Output known-1 bits are only known if set in both the LHS & RHS. + Known.One &= Known2.One; + // Output known-0 are known to be clear if zero in either the LHS | RHS. + Known.Zero |= Known2.Zero; + break; + } + case TargetOpcode::G_OR: { + // If either the LHS or the RHS are Zero, the result is zero. + computeKnownBitsImpl(MI.getOperand(2).getReg(), Known, DemandedElts, + Depth + 1); + computeKnownBitsImpl(MI.getOperand(1).getReg(), Known2, DemandedElts, + Depth + 1); + + // Output known-0 bits are only known if clear in both the LHS & RHS. + Known.Zero &= Known2.Zero; + // Output known-1 are known to be set if set in either the LHS | RHS. + Known.One |= Known2.One; + break; + } + case TargetOpcode::G_MUL: { + computeKnownBitsImpl(MI.getOperand(2).getReg(), Known, DemandedElts, + Depth + 1); + computeKnownBitsImpl(MI.getOperand(1).getReg(), Known2, DemandedElts, + Depth + 1); + // If low bits are zero in either operand, output low known-0 bits. + // Also compute a conservative estimate for high known-0 bits. + // More trickiness is possible, but this is sufficient for the + // interesting case of alignment computation. + unsigned TrailZ = + Known.countMinTrailingZeros() + Known2.countMinTrailingZeros(); + unsigned LeadZ = + std::max(Known.countMinLeadingZeros() + Known2.countMinLeadingZeros(), + BitWidth) - + BitWidth; + + Known.resetAll(); + Known.Zero.setLowBits(std::min(TrailZ, BitWidth)); + Known.Zero.setHighBits(std::min(LeadZ, BitWidth)); + break; + } + case TargetOpcode::G_SELECT: { + computeKnownBitsImpl(MI.getOperand(3).getReg(), Known, DemandedElts, + Depth + 1); + // If we don't know any bits, early out. + if (Known.isUnknown()) + break; + computeKnownBitsImpl(MI.getOperand(2).getReg(), Known2, DemandedElts, + Depth + 1); + // Only known if known in both the LHS and RHS. + Known.One &= Known2.One; + Known.Zero &= Known2.Zero; + break; + } + case TargetOpcode::G_FCMP: + case TargetOpcode::G_ICMP: { + if (TL->getBooleanContents(DstTy.isVector(), + Opcode == TargetOpcode::G_FCMP) == + TargetLowering::ZeroOrOneBooleanContent && + BitWidth > 1) + Known.Zero.setBitsFrom(1); + break; + } + case TargetOpcode::G_SEXT: { + computeKnownBitsImpl(MI.getOperand(1).getReg(), Known, DemandedElts, + Depth + 1); + // If the sign bit is known to be zero or one, then sext will extend + // it to the top bits, else it will just zext. + Known = Known.sext(BitWidth); + break; + } + case TargetOpcode::G_ANYEXT: { + computeKnownBitsImpl(MI.getOperand(1).getReg(), Known, DemandedElts, + Depth + 1); + Known = Known.zext(BitWidth, true /* ExtendedBitsAreKnownZero */); + break; + } + case TargetOpcode::G_LOAD: { + if (MI.hasOneMemOperand()) { + const MachineMemOperand *MMO = *MI.memoperands_begin(); + if (const MDNode *Ranges = MMO->getRanges()) { + computeKnownBitsFromRangeMetadata(*Ranges, Known); + } + } + break; + } + case TargetOpcode::G_ZEXTLOAD: { + // Everything above the retrieved bits is zero + if (MI.hasOneMemOperand()) + Known.Zero.setBitsFrom((*MI.memoperands_begin())->getSizeInBits()); + break; + } + case TargetOpcode::G_ASHR: + case TargetOpcode::G_LSHR: + case TargetOpcode::G_SHL: { + KnownBits RHSKnown; + computeKnownBitsImpl(MI.getOperand(2).getReg(), RHSKnown, DemandedElts, + Depth + 1); + if (!RHSKnown.isConstant()) { + LLVM_DEBUG( + MachineInstr *RHSMI = MRI->getVRegDef(MI.getOperand(2).getReg()); + dbgs() << '[' << Depth << "] Shift not known constant: " << *RHSMI); + break; + } + uint64_t Shift = RHSKnown.getConstant().getZExtValue(); + LLVM_DEBUG(dbgs() << '[' << Depth << "] Shift is " << Shift << '\n'); + + computeKnownBitsImpl(MI.getOperand(1).getReg(), Known, DemandedElts, + Depth + 1); + + switch (Opcode) { + case TargetOpcode::G_ASHR: + Known.Zero = Known.Zero.ashr(Shift); + Known.One = Known.One.ashr(Shift); + break; + case TargetOpcode::G_LSHR: + Known.Zero = Known.Zero.lshr(Shift); + Known.One = Known.One.lshr(Shift); + Known.Zero.setBitsFrom(Known.Zero.getBitWidth() - Shift); + break; + case TargetOpcode::G_SHL: + Known.Zero = Known.Zero.shl(Shift); + Known.One = Known.One.shl(Shift); + Known.Zero.setBits(0, Shift); + break; + } + break; + } + case TargetOpcode::G_INTTOPTR: + case TargetOpcode::G_PTRTOINT: + // Fall through and handle them the same as zext/trunc. + LLVM_FALLTHROUGH; + case TargetOpcode::G_ZEXT: + case TargetOpcode::G_TRUNC: { + Register SrcReg = MI.getOperand(1).getReg(); + LLT SrcTy = MRI->getType(SrcReg); + unsigned SrcBitWidth = SrcTy.isPointer() + ? DL->getIndexSize(SrcTy.getAddressSpace()) + : SrcTy.getSizeInBits(); + assert(SrcBitWidth && "SrcBitWidth can't be zero"); + Known = Known.zextOrTrunc(SrcBitWidth, true); + computeKnownBitsImpl(SrcReg, Known, DemandedElts, Depth + 1); + Known = Known.zextOrTrunc(BitWidth, true); + if (BitWidth > SrcBitWidth) + Known.Zero.setBitsFrom(SrcBitWidth); + break; + } + } + + assert(!Known.hasConflict() && "Bits known to be one AND zero?"); + LLVM_DEBUG(dbgs() << "[" << Depth << "] Compute known bits: " << MI << "[" + << Depth << "] Computed for: " << MI << "[" << Depth + << "] Known: 0x" + << (Known.Zero | Known.One).toString(16, false) << "\n" + << "[" << Depth << "] Zero: 0x" + << Known.Zero.toString(16, false) << "\n" + << "[" << Depth << "] One: 0x" + << Known.One.toString(16, false) << "\n"); +} + +void GISelKnownBitsAnalysis::getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); + MachineFunctionPass::getAnalysisUsage(AU); +} + +bool GISelKnownBitsAnalysis::runOnMachineFunction(MachineFunction &MF) { + Info.setMF(MF); + return false; +} Index: llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -2518,6 +2518,12 @@ Known.resetAll(); } +void TargetLowering::computeKnownBitsForTargetInstr( + Register R, KnownBits &Known, const APInt &DemandedElts, + const MachineRegisterInfo &MRI, unsigned Depth) const { + Known.resetAll(); +} + void TargetLowering::computeKnownBitsForFrameIndex(const SDValue Op, KnownBits &Known, const APInt &DemandedElts, Index: llvm/lib/Target/AArch64/AArch64PreLegalizerCombiner.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64PreLegalizerCombiner.cpp +++ llvm/lib/Target/AArch64/AArch64PreLegalizerCombiner.cpp @@ -15,6 +15,7 @@ #include "llvm/CodeGen/GlobalISel/Combiner.h" #include "llvm/CodeGen/GlobalISel/CombinerHelper.h" #include "llvm/CodeGen/GlobalISel/CombinerInfo.h" +#include "llvm/CodeGen/GlobalISel/GISelKnownBits.h" #include "llvm/CodeGen/GlobalISel/MIPatternMatch.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/TargetPassConfig.h" @@ -27,10 +28,14 @@ namespace { class AArch64PreLegalizerCombinerInfo : public CombinerInfo { + GISelKnownBits *KB; + public: - AArch64PreLegalizerCombinerInfo(bool EnableOpt, bool OptSize, bool MinSize) + AArch64PreLegalizerCombinerInfo(bool EnableOpt, bool OptSize, bool MinSize, + GISelKnownBits *KB) : CombinerInfo(/*AllowIllegalOps*/ true, /*ShouldLegalizeIllegal*/ false, - /*LegalizerInfo*/ nullptr, EnableOpt, OptSize, MinSize) {} + /*LegalizerInfo*/ nullptr, EnableOpt, OptSize, MinSize), + KB(KB) {} virtual bool combine(GISelChangeObserver &Observer, MachineInstr &MI, MachineIRBuilder &B) const override; }; @@ -38,7 +43,7 @@ bool AArch64PreLegalizerCombinerInfo::combine(GISelChangeObserver &Observer, MachineInstr &MI, MachineIRBuilder &B) const { - CombinerHelper Helper(Observer, B); + CombinerHelper Helper(Observer, B, KB); switch (MI.getOpcode()) { default: @@ -89,6 +94,8 @@ AU.addRequired(); AU.setPreservesCFG(); getSelectionDAGFallbackAnalysisUsage(AU); + AU.addRequired(); + AU.addPreserved(); MachineFunctionPass::getAnalysisUsage(AU); } @@ -104,8 +111,9 @@ const Function &F = MF.getFunction(); bool EnableOpt = MF.getTarget().getOptLevel() != CodeGenOpt::None && !skipFunction(F); + GISelKnownBits *KB = &getAnalysis().get(); AArch64PreLegalizerCombinerInfo PCInfo(EnableOpt, F.hasOptSize(), - F.hasMinSize()); + F.hasMinSize(), KB); Combiner C(PCInfo, TPC); return C.combineMachineInstrs(MF, /*CSEInfo*/ nullptr); } @@ -115,6 +123,7 @@ "Combine AArch64 machine instrs before legalization", false, false) INITIALIZE_PASS_DEPENDENCY(TargetPassConfig) +INITIALIZE_PASS_DEPENDENCY(GISelKnownBitsAnalysis) INITIALIZE_PASS_END(AArch64PreLegalizerCombiner, DEBUG_TYPE, "Combine AArch64 machine instrs before legalization", false, false) Index: llvm/test/CodeGen/AArch64/GlobalISel/gisel-commandline-option.ll =================================================================== --- llvm/test/CodeGen/AArch64/GlobalISel/gisel-commandline-option.ll +++ llvm/test/CodeGen/AArch64/GlobalISel/gisel-commandline-option.ll @@ -43,6 +43,7 @@ ; ENABLED: IRTranslator ; VERIFY-NEXT: Verify generated machine code +; ENABLED-NEXT: Analysis for ComputingKnownBits ; ENABLED-NEXT: PreLegalizerCombiner ; VERIFY-NEXT: Verify generated machine code ; ENABLED-NEXT: Analysis containing CSE Info Index: llvm/unittests/CodeGen/GlobalISel/CMakeLists.txt =================================================================== --- llvm/unittests/CodeGen/GlobalISel/CMakeLists.txt +++ llvm/unittests/CodeGen/GlobalISel/CMakeLists.txt @@ -16,4 +16,5 @@ MachineIRBuilderTest.cpp GISelMITest.cpp PatternMatchTest.cpp + KnownBitsTest.cpp ) Index: llvm/unittests/CodeGen/GlobalISel/CSETest.cpp =================================================================== --- llvm/unittests/CodeGen/GlobalISel/CSETest.cpp +++ llvm/unittests/CodeGen/GlobalISel/CSETest.cpp @@ -12,6 +12,7 @@ namespace { TEST_F(GISelMITest, TestCSE) { + setUp(); if (!TM) return; @@ -74,6 +75,7 @@ } TEST_F(GISelMITest, TestCSEConstantConfig) { + setUp(); if (!TM) return; Index: llvm/unittests/CodeGen/GlobalISel/GISelMITest.h =================================================================== --- llvm/unittests/CodeGen/GlobalISel/GISelMITest.h +++ llvm/unittests/CodeGen/GlobalISel/GISelMITest.h @@ -135,11 +135,12 @@ class GISelMITest : public ::testing::Test { protected: - GISelMITest() : ::testing::Test() { + GISelMITest() : ::testing::Test() {} + void setUp(StringRef ExtraAssembly = "") { TM = createTargetMachine(); if (!TM) return; - ModuleMMIPair = createDummyModule(Context, *TM, ""); + ModuleMMIPair = createDummyModule(Context, *TM, ExtraAssembly); MF = getMFFromMMI(ModuleMMIPair.first.get(), ModuleMMIPair.second.get()); collectCopies(Copies, MF); EntryMBB = &*MF->begin(); Index: llvm/unittests/CodeGen/GlobalISel/KnownBitsTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/CodeGen/GlobalISel/KnownBitsTest.cpp @@ -0,0 +1,56 @@ +//===- KnownBitsTest.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 +// +//===----------------------------------------------------------------------===// + +#include "GISelMITest.h" +#include "llvm/CodeGen/GlobalISel/GISelKnownBits.h" +#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" + +TEST_F(GISelMITest, TestKnownBitsCst) { + StringRef MIRString = " %3:_(s8) = G_CONSTANT i8 1\n" + " %4:_(s8) = COPY %3\n"; + setUp(MIRString); + if (!TM) + return; + unsigned CopyReg = Copies[Copies.size() - 1]; + MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg); + unsigned SrcReg = FinalCopy->getOperand(1).getReg(); + GISelKnownBits Info; + Info.setMF(*MF); + KnownBits Res = Info.getKnownBits(SrcReg); + EXPECT_EQ((uint64_t)1, Res.One.getZExtValue()); + EXPECT_EQ((uint64_t)0xfe, Res.Zero.getZExtValue()); +} + +TEST_F(GISelMITest, TestKnownBits) { + + StringRef MIR = " %3:_(s32) = G_TRUNC %0\n" + " %4:_(s32) = G_TRUNC %1\n" + " %5:_(s32) = G_CONSTANT i32 5\n" + " %6:_(s32) = G_CONSTANT i32 24\n" + " %7:_(s32) = G_CONSTANT i32 28\n" + " %8:_(s32) = G_SHL %3, %5\n" + " %9:_(s32) = G_SHL %4, %5\n" + " %10:_(s32) = G_OR %8, %6\n" + " %11:_(s32) = G_OR %9, %7\n" + " %12:_(s32) = G_MUL %10, %11\n" + " %13:_(s32) = COPY %12\n"; + setUp(MIR); + if (!TM) + return; + unsigned CopyReg = Copies[Copies.size() - 1]; + MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg); + unsigned SrcReg = FinalCopy->getOperand(1).getReg(); + GISelKnownBits Info; + Info.setMF(*MF); + KnownBits Known = Info.getKnownBits(SrcReg); + EXPECT_FALSE(Known.hasConflict()); + EXPECT_EQ(0u, Known.One.getZExtValue()); + EXPECT_EQ(31u, Known.Zero.getZExtValue()); + APInt Zeroes = Info.getKnownZeroes(SrcReg); + EXPECT_EQ(Known.Zero, Zeroes); +} Index: llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp =================================================================== --- llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp +++ llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp @@ -26,6 +26,7 @@ // Test CTTZ expansion when CTTZ_ZERO_UNDEF is legal or custom, // in which case it becomes CTTZ_ZERO_UNDEF with select. TEST_F(GISelMITest, LowerBitCountingCTTZ0) { + setUp(); if (!TM) return; @@ -57,6 +58,7 @@ // CTTZ expansion in terms of CTLZ TEST_F(GISelMITest, LowerBitCountingCTTZ1) { + setUp(); if (!TM) return; @@ -90,6 +92,7 @@ // CTTZ expansion in terms of CTPOP TEST_F(GISelMITest, LowerBitCountingCTTZ2) { + setUp(); if (!TM) return; @@ -185,6 +188,7 @@ // CTTZ_ZERO_UNDEF expansion in terms of CTTZ TEST_F(GISelMITest, LowerBitCountingCTTZ3) { + setUp(); if (!TM) return; @@ -211,6 +215,7 @@ // CTLZ expansion in terms of CTLZ_ZERO_UNDEF TEST_F(GISelMITest, LowerBitCountingCTLZ0) { + setUp(); if (!TM) return; @@ -241,6 +246,7 @@ // CTLZ expansion in terms of CTLZ_ZERO_UNDEF if the latter is a libcall TEST_F(GISelMITest, LowerBitCountingCTLZLibcall) { + setUp(); if (!TM) return; @@ -271,6 +277,7 @@ // CTLZ expansion TEST_F(GISelMITest, LowerBitCountingCTLZ1) { + setUp(); if (!TM) return; @@ -311,6 +318,7 @@ // CTLZ widening. TEST_F(GISelMITest, WidenBitCountingCTLZ) { + setUp(); if (!TM) return; @@ -345,6 +353,7 @@ // CTLZ_ZERO_UNDEF widening. TEST_F(GISelMITest, WidenBitCountingCTLZZeroUndef) { + setUp(); if (!TM) return; @@ -380,6 +389,7 @@ // CTPOP widening. TEST_F(GISelMITest, WidenBitCountingCTPOP) { + setUp(); if (!TM) return; @@ -412,6 +422,7 @@ // CTTZ_ZERO_UNDEF widening. TEST_F(GISelMITest, WidenBitCountingCTTZ_ZERO_UNDEF) { + setUp(); if (!TM) return; @@ -445,6 +456,7 @@ // CTTZ widening. TEST_F(GISelMITest, WidenBitCountingCTTZ) { + setUp(); if (!TM) return; @@ -478,6 +490,7 @@ } // UADDO widening. TEST_F(GISelMITest, WidenUADDO) { + setUp(); if (!TM) return; @@ -516,6 +529,7 @@ // USUBO widening. TEST_F(GISelMITest, WidenUSUBO) { + setUp(); if (!TM) return; Index: llvm/unittests/CodeGen/GlobalISel/MachineIRBuilderTest.cpp =================================================================== --- llvm/unittests/CodeGen/GlobalISel/MachineIRBuilderTest.cpp +++ llvm/unittests/CodeGen/GlobalISel/MachineIRBuilderTest.cpp @@ -10,6 +10,7 @@ #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" TEST_F(GISelMITest, TestBuildConstantFConstant) { + setUp(); if (!TM) return; @@ -41,6 +42,7 @@ #ifndef NDEBUG TEST_F(GISelMITest, TestBuildConstantFConstantDeath) { + setUp(); if (!TM) return;