//===- HexagonConstPropagation.cpp ----------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #define DEBUG_TYPE "hcp" #include "HexagonInstrInfo.h" #include "HexagonRegisterInfo.h" #include "HexagonSubtarget.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Type.h" #include "llvm/Pass.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include #include #include #include using namespace llvm; namespace { // Properties of a value that are tracked by the propagation. // A property that is marked as present (i.e. bit is set) dentes that the // value is known (proven) to have this property. Not all combinations // of bits make sense, for example Zero and NonZero are mutually exclusive, // but on the other hand, Zero implies Finite. In this case, whenever // the Zero property is present, Finite should also be present. class ConstantProperties { public: enum { Unknown = 0x0000, Zero = 0x0001, NonZero = 0x0002, Finite = 0x0004, Infinity = 0x0008, NaN = 0x0010, SignedZero = 0x0020, NumericProperties = (Zero|NonZero|Finite|Infinity|NaN|SignedZero), PosOrZero = 0x0100, NegOrZero = 0x0200, SignProperties = (PosOrZero|NegOrZero), Everything = (NumericProperties|SignProperties) }; // For a given constant, deduce the set of trackable properties that this // constant has. static uint32_t deduce(const Constant *C); }; // A representation of a register as it can appear in a MachineOperand, // i.e. a pair register:subregister. struct Register { unsigned Reg, SubReg; explicit Register(unsigned R, unsigned SR = 0) : Reg(R), SubReg(SR) {} explicit Register(const MachineOperand &MO) : Reg(MO.getReg()), SubReg(MO.getSubReg()) {} void print(const TargetRegisterInfo *TRI = nullptr) const { dbgs() << printReg(Reg, TRI, SubReg); } bool operator== (const Register &R) const { return (Reg == R.Reg) && (SubReg == R.SubReg); } }; // Lattice cell, based on that was described in the W-Z paper on constant // propagation. // Latice cell will be allowed to hold multiple constant values. While // multiple values would normally indicate "bottom", we can still derive // some useful information from them. For example, comparison X > 0 // could be folded if all the values in the cell associated with X are // positive. class LatticeCell { private: enum { Normal, Top, Bottom }; static const unsigned MaxCellSize = 4; unsigned Kind:2; unsigned Size:3; unsigned IsSpecial:1; unsigned :0; public: union { uint32_t Properties; const Constant *Value; const Constant *Values[MaxCellSize]; }; LatticeCell() : Kind(Top), Size(0), IsSpecial(false) { for (unsigned i = 0; i < MaxCellSize; ++i) Values[i] = nullptr; } bool meet(const LatticeCell &L); bool add(const Constant *C); bool add(uint32_t Property); uint32_t properties() const; unsigned size() const { return Size; } LatticeCell &operator= (const LatticeCell &L) { if (this != &L) { // This memcpy also copies Properties (when L.Size == 0). uint32_t N = L.IsSpecial ? sizeof L.Properties : L.Size*sizeof(const Constant*); memcpy(Values, L.Values, N); Kind = L.Kind; Size = L.Size; IsSpecial = L.IsSpecial; } return *this; } bool isSingle() const { return size() == 1; } bool isProperty() const { return IsSpecial; } bool isTop() const { return Kind == Top; } bool isBottom() const { return Kind == Bottom; } bool setBottom() { bool Changed = (Kind != Bottom); Kind = Bottom; Size = 0; IsSpecial = false; return Changed; } void print(raw_ostream &os) const; private: void setProperty() { IsSpecial = true; Size = 0; Kind = Normal; } bool convertToProperty(); }; #ifndef NDEBUG raw_ostream &operator<< (raw_ostream &os, const LatticeCell &L) { L.print(os); return os; } #endif class MachineConstEvaluator; class MachineConstPropagator { public: MachineConstPropagator(MachineConstEvaluator &E) : MCE(E) { Bottom.setBottom(); } // Mapping: vreg -> cell // The keys are registers _without_ subregisters. This won't allow // definitions in the form of "vreg:subreg = ...". Such definitions // would be questionable from the point of view of SSA, since the "vreg" // could not be initialized in its entirety (specifically, an instruction // defining the "other part" of "vreg" would also count as a definition // of "vreg", which would violate the SSA). // If a value of a pair vreg:subreg needs to be obtained, the cell for // "vreg" needs to be looked up, and then the value of subregister "subreg" // needs to be evaluated. class CellMap { public: CellMap() { assert(Top.isTop()); Bottom.setBottom(); } void clear() { Map.clear(); } bool has(unsigned R) const { // All non-virtual registers are considered "bottom". if (!TargetRegisterInfo::isVirtualRegister(R)) return true; MapType::const_iterator F = Map.find(R); return F != Map.end(); } const LatticeCell &get(unsigned R) const { if (!TargetRegisterInfo::isVirtualRegister(R)) return Bottom; MapType::const_iterator F = Map.find(R); if (F != Map.end()) return F->second; return Top; } // Invalidates any const references. void update(unsigned R, const LatticeCell &L) { Map[R] = L; } void print(raw_ostream &os, const TargetRegisterInfo &TRI) const; private: using MapType = std::map; MapType Map; // To avoid creating "top" entries, return a const reference to // this cell in "get". Also, have a "Bottom" cell to return from // get when a value of a physical register is requested. LatticeCell Top, Bottom; public: using const_iterator = MapType::const_iterator; const_iterator begin() const { return Map.begin(); } const_iterator end() const { return Map.end(); } }; bool run(MachineFunction &MF); private: void visitPHI(const MachineInstr &PN); void visitNonBranch(const MachineInstr &MI); void visitBranchesFrom(const MachineInstr &BrI); void visitUsesOf(unsigned R); bool computeBlockSuccessors(const MachineBasicBlock *MB, SetVector &Targets); void removeCFGEdge(MachineBasicBlock *From, MachineBasicBlock *To); void propagate(MachineFunction &MF); bool rewrite(MachineFunction &MF); MachineRegisterInfo *MRI; MachineConstEvaluator &MCE; using CFGEdge = std::pair; using SetOfCFGEdge = std::set; using SetOfInstr = std::set; using QueueOfCFGEdge = std::queue; LatticeCell Bottom; CellMap Cells; SetOfCFGEdge EdgeExec; SetOfInstr InstrExec; QueueOfCFGEdge FlowQ; }; // The "evaluator/rewriter" of machine instructions. This is an abstract // base class that provides the interface that the propagator will use, // as well as some helper functions that are target-independent. class MachineConstEvaluator { public: MachineConstEvaluator(MachineFunction &Fn) : TRI(*Fn.getSubtarget().getRegisterInfo()), MF(Fn), CX(Fn.getFunction().getContext()) {} virtual ~MachineConstEvaluator() = default; // The required interface: // - A set of three "evaluate" functions. Each returns "true" if the // computation succeeded, "false" otherwise. // (1) Given an instruction MI, and the map with input values "Inputs", // compute the set of output values "Outputs". An example of when // the computation can "fail" is if MI is not an instruction that // is recognized by the evaluator. // (2) Given a register R (as reg:subreg), compute the cell that // corresponds to the "subreg" part of the given register. // (3) Given a branch instruction BrI, compute the set of target blocks. // If the branch can fall-through, add null (0) to the list of // possible targets. // - A function "rewrite", that given the cell map after propagation, // could rewrite instruction MI in a more beneficial form. Return // "true" if a change has been made, "false" otherwise. using CellMap = MachineConstPropagator::CellMap; virtual bool evaluate(const MachineInstr &MI, const CellMap &Inputs, CellMap &Outputs) = 0; virtual bool evaluate(const Register &R, const LatticeCell &SrcC, LatticeCell &Result) = 0; virtual bool evaluate(const MachineInstr &BrI, const CellMap &Inputs, SetVector &Targets, bool &CanFallThru) = 0; virtual bool rewrite(MachineInstr &MI, const CellMap &Inputs) = 0; const TargetRegisterInfo &TRI; protected: MachineFunction &MF; LLVMContext &CX; struct Comparison { enum { Unk = 0x00, EQ = 0x01, NE = 0x02, L = 0x04, // Less-than property. G = 0x08, // Greater-than property. U = 0x40, // Unsigned property. LTs = L, LEs = L | EQ, GTs = G, GEs = G | EQ, LTu = L | U, LEu = L | EQ | U, GTu = G | U, GEu = G | EQ | U }; static uint32_t negate(uint32_t Cmp) { if (Cmp == EQ) return NE; if (Cmp == NE) return EQ; assert((Cmp & (L|G)) != (L|G)); return Cmp ^ (L|G); } }; // Helper functions. bool getCell(const Register &R, const CellMap &Inputs, LatticeCell &RC); bool constToInt(const Constant *C, APInt &Val) const; bool constToFloat(const Constant *C, APFloat &Val) const; const ConstantInt *intToConst(const APInt &Val) const; // Compares. bool evaluateCMPrr(uint32_t Cmp, const Register &R1, const Register &R2, const CellMap &Inputs, bool &Result); bool evaluateCMPri(uint32_t Cmp, const Register &R1, const APInt &A2, const CellMap &Inputs, bool &Result); bool evaluateCMPrp(uint32_t Cmp, const Register &R1, uint64_t Props2, const CellMap &Inputs, bool &Result); bool evaluateCMPii(uint32_t Cmp, const APInt &A1, const APInt &A2, bool &Result); bool evaluateCMPpi(uint32_t Cmp, uint32_t Props, const APInt &A2, bool &Result); bool evaluateCMPpp(uint32_t Cmp, uint32_t Props1, uint32_t Props2, bool &Result); bool evaluateCOPY(const Register &R1, const CellMap &Inputs, LatticeCell &Result); // Logical operations. bool evaluateANDrr(const Register &R1, const Register &R2, const CellMap &Inputs, LatticeCell &Result); bool evaluateANDri(const Register &R1, const APInt &A2, const CellMap &Inputs, LatticeCell &Result); bool evaluateANDii(const APInt &A1, const APInt &A2, APInt &Result); bool evaluateORrr(const Register &R1, const Register &R2, const CellMap &Inputs, LatticeCell &Result); bool evaluateORri(const Register &R1, const APInt &A2, const CellMap &Inputs, LatticeCell &Result); bool evaluateORii(const APInt &A1, const APInt &A2, APInt &Result); bool evaluateXORrr(const Register &R1, const Register &R2, const CellMap &Inputs, LatticeCell &Result); bool evaluateXORri(const Register &R1, const APInt &A2, const CellMap &Inputs, LatticeCell &Result); bool evaluateXORii(const APInt &A1, const APInt &A2, APInt &Result); // Extensions. bool evaluateZEXTr(const Register &R1, unsigned Width, unsigned Bits, const CellMap &Inputs, LatticeCell &Result); bool evaluateZEXTi(const APInt &A1, unsigned Width, unsigned Bits, APInt &Result); bool evaluateSEXTr(const Register &R1, unsigned Width, unsigned Bits, const CellMap &Inputs, LatticeCell &Result); bool evaluateSEXTi(const APInt &A1, unsigned Width, unsigned Bits, APInt &Result); // Leading/trailing bits. bool evaluateCLBr(const Register &R1, bool Zeros, bool Ones, const CellMap &Inputs, LatticeCell &Result); bool evaluateCLBi(const APInt &A1, bool Zeros, bool Ones, APInt &Result); bool evaluateCTBr(const Register &R1, bool Zeros, bool Ones, const CellMap &Inputs, LatticeCell &Result); bool evaluateCTBi(const APInt &A1, bool Zeros, bool Ones, APInt &Result); // Bitfield extract. bool evaluateEXTRACTr(const Register &R1, unsigned Width, unsigned Bits, unsigned Offset, bool Signed, const CellMap &Inputs, LatticeCell &Result); bool evaluateEXTRACTi(const APInt &A1, unsigned Bits, unsigned Offset, bool Signed, APInt &Result); // Vector operations. bool evaluateSplatr(const Register &R1, unsigned Bits, unsigned Count, const CellMap &Inputs, LatticeCell &Result); bool evaluateSplati(const APInt &A1, unsigned Bits, unsigned Count, APInt &Result); }; } // end anonymous namespace uint32_t ConstantProperties::deduce(const Constant *C) { if (isa(C)) { const ConstantInt *CI = cast(C); if (CI->isZero()) return Zero | PosOrZero | NegOrZero | Finite; uint32_t Props = (NonZero | Finite); if (CI->isNegative()) return Props | NegOrZero; return Props | PosOrZero; } if (isa(C)) { const ConstantFP *CF = cast(C); uint32_t Props = CF->isNegative() ? (NegOrZero|NonZero) : PosOrZero; if (CF->isZero()) return (Props & ~NumericProperties) | (Zero|Finite); Props = (Props & ~NumericProperties) | NonZero; if (CF->isNaN()) return (Props & ~NumericProperties) | NaN; const APFloat &Val = CF->getValueAPF(); if (Val.isInfinity()) return (Props & ~NumericProperties) | Infinity; Props |= Finite; return Props; } return Unknown; } // Convert a cell from a set of specific values to a cell that tracks // properties. bool LatticeCell::convertToProperty() { if (isProperty()) return false; // Corner case: converting a fresh (top) cell to "special". // This can happen, when adding a property to a top cell. uint32_t Everything = ConstantProperties::Everything; uint32_t Ps = !isTop() ? properties() : Everything; if (Ps != ConstantProperties::Unknown) { Properties = Ps; setProperty(); } else { setBottom(); } return true; } #ifndef NDEBUG void LatticeCell::print(raw_ostream &os) const { if (isProperty()) { os << "{ "; uint32_t Ps = properties(); if (Ps & ConstantProperties::Zero) os << "zero "; if (Ps & ConstantProperties::NonZero) os << "nonzero "; if (Ps & ConstantProperties::Finite) os << "finite "; if (Ps & ConstantProperties::Infinity) os << "infinity "; if (Ps & ConstantProperties::NaN) os << "nan "; if (Ps & ConstantProperties::PosOrZero) os << "poz "; if (Ps & ConstantProperties::NegOrZero) os << "nez "; os << '}'; return; } os << "{ "; if (isBottom()) { os << "bottom"; } else if (isTop()) { os << "top"; } else { for (unsigned i = 0; i < size(); ++i) { const Constant *C = Values[i]; if (i != 0) os << ", "; C->print(os); } } os << " }"; } #endif // "Meet" operation on two cells. This is the key of the propagation // algorithm. bool LatticeCell::meet(const LatticeCell &L) { bool Changed = false; if (L.isBottom()) Changed = setBottom(); if (isBottom() || L.isTop()) return Changed; if (isTop()) { *this = L; // L can be neither Top nor Bottom, so *this must have changed. return true; } // Top/bottom cases covered. Need to integrate L's set into ours. if (L.isProperty()) return add(L.properties()); for (unsigned i = 0; i < L.size(); ++i) { const Constant *LC = L.Values[i]; Changed |= add(LC); } return Changed; } // Add a new constant to the cell. This is actually where the cell update // happens. If a cell has room for more constants, the new constant is added. // Otherwise, the cell is converted to a "property" cell (i.e. a cell that // will track properties of the associated values, and not the values // themselves. Care is taken to handle special cases, like "bottom", etc. bool LatticeCell::add(const Constant *LC) { assert(LC); if (isBottom()) return false; if (!isProperty()) { // Cell is not special. Try to add the constant here first, // if there is room. unsigned Index = 0; while (Index < Size) { const Constant *C = Values[Index]; // If the constant is already here, no change is needed. if (C == LC) return false; Index++; } if (Index < MaxCellSize) { Values[Index] = LC; Kind = Normal; Size++; return true; } } bool Changed = false; // This cell is special, or is not special, but is full. After this // it will be special. Changed = convertToProperty(); uint32_t Ps = properties(); uint32_t NewPs = Ps & ConstantProperties::deduce(LC); if (NewPs == ConstantProperties::Unknown) { setBottom(); return true; } if (Ps != NewPs) { Properties = NewPs; Changed = true; } return Changed; } // Add a property to the cell. This will force the cell to become a property- // tracking cell. bool LatticeCell::add(uint32_t Property) { bool Changed = convertToProperty(); uint32_t Ps = properties(); if (Ps == (Ps & Property)) return Changed; Properties = Property & Ps; return true; } // Return the properties of the values in the cell. This is valid for any // cell, and does not alter the cell itself. uint32_t LatticeCell::properties() const { if (isProperty()) return Properties; assert(!isTop() && "Should not call this for a top cell"); if (isBottom()) return ConstantProperties::Unknown; assert(size() > 0 && "Empty cell"); uint32_t Ps = ConstantProperties::deduce(Values[0]); for (unsigned i = 1; i < size(); ++i) { if (Ps == ConstantProperties::Unknown) break; Ps &= ConstantProperties::deduce(Values[i]); } return Ps; } #ifndef NDEBUG void MachineConstPropagator::CellMap::print(raw_ostream &os, const TargetRegisterInfo &TRI) const { for (auto &I : Map) dbgs() << " " << printReg(I.first, &TRI) << " -> " << I.second << '\n'; } #endif void MachineConstPropagator::visitPHI(const MachineInstr &PN) { const MachineBasicBlock *MB = PN.getParent(); unsigned MBN = MB->getNumber(); LLVM_DEBUG(dbgs() << "Visiting FI(" << printMBBReference(*MB) << "): " << PN); const MachineOperand &MD = PN.getOperand(0); Register DefR(MD); assert(TargetRegisterInfo::isVirtualRegister(DefR.Reg)); bool Changed = false; // If the def has a sub-register, set the corresponding cell to "bottom". if (DefR.SubReg) { Bottomize: const LatticeCell &T = Cells.get(DefR.Reg); Changed = !T.isBottom(); Cells.update(DefR.Reg, Bottom); if (Changed) visitUsesOf(DefR.Reg); return; } LatticeCell DefC = Cells.get(DefR.Reg); for (unsigned i = 1, n = PN.getNumOperands(); i < n; i += 2) { const MachineBasicBlock *PB = PN.getOperand(i+1).getMBB(); unsigned PBN = PB->getNumber(); if (!EdgeExec.count(CFGEdge(PBN, MBN))) { LLVM_DEBUG(dbgs() << " edge " << printMBBReference(*PB) << "->" << printMBBReference(*MB) << " not executable\n"); continue; } const MachineOperand &SO = PN.getOperand(i); Register UseR(SO); // If the input is not a virtual register, we don't really know what // value it holds. if (!TargetRegisterInfo::isVirtualRegister(UseR.Reg)) goto Bottomize; // If there is no cell for an input register, it means top. if (!Cells.has(UseR.Reg)) continue; LatticeCell SrcC; bool Eval = MCE.evaluate(UseR, Cells.get(UseR.Reg), SrcC); LLVM_DEBUG(dbgs() << " edge from " << printMBBReference(*PB) << ": " << printReg(UseR.Reg, &MCE.TRI, UseR.SubReg) << SrcC << '\n'); Changed |= Eval ? DefC.meet(SrcC) : DefC.setBottom(); Cells.update(DefR.Reg, DefC); if (DefC.isBottom()) break; } if (Changed) visitUsesOf(DefR.Reg); } void MachineConstPropagator::visitNonBranch(const MachineInstr &MI) { LLVM_DEBUG(dbgs() << "Visiting MI(" << printMBBReference(*MI.getParent()) << "): " << MI); CellMap Outputs; bool Eval = MCE.evaluate(MI, Cells, Outputs); LLVM_DEBUG({ if (Eval) { dbgs() << " outputs:"; for (auto &I : Outputs) dbgs() << ' ' << I.second; dbgs() << '\n'; } }); // Update outputs. If the value was not computed, set all the // def cells to bottom. for (const MachineOperand &MO : MI.operands()) { if (!MO.isReg() || !MO.isDef()) continue; Register DefR(MO); // Only track virtual registers. if (!TargetRegisterInfo::isVirtualRegister(DefR.Reg)) continue; bool Changed = false; // If the evaluation failed, set cells for all output registers to bottom. if (!Eval) { const LatticeCell &T = Cells.get(DefR.Reg); Changed = !T.isBottom(); Cells.update(DefR.Reg, Bottom); } else { // Find the corresponding cell in the computed outputs. // If it's not there, go on to the next def. if (!Outputs.has(DefR.Reg)) continue; LatticeCell RC = Cells.get(DefR.Reg); Changed = RC.meet(Outputs.get(DefR.Reg)); Cells.update(DefR.Reg, RC); } if (Changed) visitUsesOf(DefR.Reg); } } // Starting at a given branch, visit remaining branches in the block. // Traverse over the subsequent branches for as long as the preceding one // can fall through. Add all the possible targets to the flow work queue, // including the potential fall-through to the layout-successor block. void MachineConstPropagator::visitBranchesFrom(const MachineInstr &BrI) { const MachineBasicBlock &B = *BrI.getParent(); unsigned MBN = B.getNumber(); MachineBasicBlock::const_iterator It = BrI.getIterator(); MachineBasicBlock::const_iterator End = B.end(); SetVector Targets; bool EvalOk = true, FallsThru = true; while (It != End) { const MachineInstr &MI = *It; InstrExec.insert(&MI); LLVM_DEBUG(dbgs() << "Visiting " << (EvalOk ? "BR" : "br") << "(" << printMBBReference(B) << "): " << MI); // Do not evaluate subsequent branches if the evaluation of any of the // previous branches failed. Keep iterating over the branches only // to mark them as executable. EvalOk = EvalOk && MCE.evaluate(MI, Cells, Targets, FallsThru); if (!EvalOk) FallsThru = true; if (!FallsThru) break; ++It; } if (EvalOk) { // Need to add all CFG successors that lead to EH landing pads. // There won't be explicit branches to these blocks, but they must // be processed. for (const MachineBasicBlock *SB : B.successors()) { if (SB->isEHPad()) Targets.insert(SB); } if (FallsThru) { const MachineFunction &MF = *B.getParent(); MachineFunction::const_iterator BI = B.getIterator(); MachineFunction::const_iterator Next = std::next(BI); if (Next != MF.end()) Targets.insert(&*Next); } } else { // If the evaluation of the branches failed, make "Targets" to be the // set of all successors of the block from the CFG. // If the evaluation succeeded for all visited branches, then if the // last one set "FallsThru", then add an edge to the layout successor // to the targets. Targets.clear(); LLVM_DEBUG(dbgs() << " failed to evaluate a branch...adding all CFG " "successors\n"); for (const MachineBasicBlock *SB : B.successors()) Targets.insert(SB); } for (const MachineBasicBlock *TB : Targets) { unsigned TBN = TB->getNumber(); LLVM_DEBUG(dbgs() << " pushing edge " << printMBBReference(B) << " -> " << printMBBReference(*TB) << "\n"); FlowQ.push(CFGEdge(MBN, TBN)); } } void MachineConstPropagator::visitUsesOf(unsigned Reg) { LLVM_DEBUG(dbgs() << "Visiting uses of " << printReg(Reg, &MCE.TRI) << Cells.get(Reg) << '\n'); for (MachineInstr &MI : MRI->use_nodbg_instructions(Reg)) { // Do not process non-executable instructions. They can become exceutable // later (via a flow-edge in the work queue). In such case, the instruc- // tion will be visited at that time. if (!InstrExec.count(&MI)) continue; if (MI.isPHI()) visitPHI(MI); else if (!MI.isBranch()) visitNonBranch(MI); else visitBranchesFrom(MI); } } bool MachineConstPropagator::computeBlockSuccessors(const MachineBasicBlock *MB, SetVector &Targets) { MachineBasicBlock::const_iterator FirstBr = MB->end(); for (const MachineInstr &MI : *MB) { if (MI.isDebugInstr()) continue; if (MI.isBranch()) { FirstBr = MI.getIterator(); break; } } Targets.clear(); MachineBasicBlock::const_iterator End = MB->end(); bool DoNext = true; for (MachineBasicBlock::const_iterator I = FirstBr; I != End; ++I) { const MachineInstr &MI = *I; // Can there be debug instructions between branches? if (MI.isDebugInstr()) continue; if (!InstrExec.count(&MI)) continue; bool Eval = MCE.evaluate(MI, Cells, Targets, DoNext); if (!Eval) return false; if (!DoNext) break; } // If the last branch could fall-through, add block's layout successor. if (DoNext) { MachineFunction::const_iterator BI = MB->getIterator(); MachineFunction::const_iterator NextI = std::next(BI); if (NextI != MB->getParent()->end()) Targets.insert(&*NextI); } // Add all the EH landing pads. for (const MachineBasicBlock *SB : MB->successors()) if (SB->isEHPad()) Targets.insert(SB); return true; } void MachineConstPropagator::removeCFGEdge(MachineBasicBlock *From, MachineBasicBlock *To) { // First, remove the CFG successor/predecessor information. From->removeSuccessor(To); // Remove all corresponding PHI operands in the To block. for (auto I = To->begin(), E = To->getFirstNonPHI(); I != E; ++I) { MachineInstr *PN = &*I; // reg0 = PHI reg1, bb2, reg3, bb4, ... int N = PN->getNumOperands()-2; while (N > 0) { if (PN->getOperand(N+1).getMBB() == From) { PN->RemoveOperand(N+1); PN->RemoveOperand(N); } N -= 2; } } } void MachineConstPropagator::propagate(MachineFunction &MF) { MachineBasicBlock *Entry = GraphTraits::getEntryNode(&MF); unsigned EntryNum = Entry->getNumber(); // Start with a fake edge, just to process the entry node. FlowQ.push(CFGEdge(EntryNum, EntryNum)); while (!FlowQ.empty()) { CFGEdge Edge = FlowQ.front(); FlowQ.pop(); LLVM_DEBUG( dbgs() << "Picked edge " << printMBBReference(*MF.getBlockNumbered(Edge.first)) << "->" << printMBBReference(*MF.getBlockNumbered(Edge.second)) << '\n'); if (Edge.first != EntryNum) if (EdgeExec.count(Edge)) continue; EdgeExec.insert(Edge); MachineBasicBlock *SB = MF.getBlockNumbered(Edge.second); // Process the block in three stages: // - visit all PHI nodes, // - visit all non-branch instructions, // - visit block branches. MachineBasicBlock::const_iterator It = SB->begin(), End = SB->end(); // Visit PHI nodes in the successor block. while (It != End && It->isPHI()) { InstrExec.insert(&*It); visitPHI(*It); ++It; } // If the successor block just became executable, visit all instructions. // To see if this is the first time we're visiting it, check the first // non-debug instruction to see if it is executable. while (It != End && It->isDebugInstr()) ++It; assert(It == End || !It->isPHI()); // If this block has been visited, go on to the next one. if (It != End && InstrExec.count(&*It)) continue; // For now, scan all non-branch instructions. Branches require different // processing. while (It != End && !It->isBranch()) { if (!It->isDebugInstr()) { InstrExec.insert(&*It); visitNonBranch(*It); } ++It; } // Time to process the end of the block. This is different from // processing regular (non-branch) instructions, because there can // be multiple branches in a block, and they can cause the block to // terminate early. if (It != End) { visitBranchesFrom(*It); } else { // If the block didn't have a branch, add all successor edges to the // work queue. (There should really be only one successor in such case.) unsigned SBN = SB->getNumber(); for (const MachineBasicBlock *SSB : SB->successors()) FlowQ.push(CFGEdge(SBN, SSB->getNumber())); } } // while (FlowQ) LLVM_DEBUG({ dbgs() << "Cells after propagation:\n"; Cells.print(dbgs(), MCE.TRI); dbgs() << "Dead CFG edges:\n"; for (const MachineBasicBlock &B : MF) { unsigned BN = B.getNumber(); for (const MachineBasicBlock *SB : B.successors()) { unsigned SN = SB->getNumber(); if (!EdgeExec.count(CFGEdge(BN, SN))) dbgs() << " " << printMBBReference(B) << " -> " << printMBBReference(*SB) << '\n'; } } }); } bool MachineConstPropagator::rewrite(MachineFunction &MF) { bool Changed = false; // Rewrite all instructions based on the collected cell information. // // Traverse the instructions in a post-order, so that rewriting an // instruction can make changes "downstream" in terms of control-flow // without affecting the rewriting process. (We should not change // instructions that have not yet been visited by the rewriter.) // The reason for this is that the rewriter can introduce new vregs, // and replace uses of old vregs (which had corresponding cells // computed during propagation) with these new vregs (which at this // point would not have any cells, and would appear to be "top"). // If an attempt was made to evaluate an instruction with a fresh // "top" vreg, it would cause an error (abend) in the evaluator. // Collect the post-order-traversal block ordering. The subsequent // traversal/rewrite will update block successors, so it's safer // if the visiting order it computed ahead of time. std::vector POT; for (MachineBasicBlock *B : post_order(&MF)) if (!B->empty()) POT.push_back(B); for (MachineBasicBlock *B : POT) { // Walk the block backwards (which usually begin with the branches). // If any branch is rewritten, we may need to update the successor // information for this block. Unless the block's successors can be // precisely determined (which may not be the case for indirect // branches), we cannot modify any branch. // Compute the successor information. SetVector Targets; bool HaveTargets = computeBlockSuccessors(B, Targets); // Rewrite the executable instructions. Skip branches if we don't // have block successor information. for (auto I = B->rbegin(), E = B->rend(); I != E; ++I) { MachineInstr &MI = *I; if (InstrExec.count(&MI)) { if (MI.isBranch() && !HaveTargets) continue; Changed |= MCE.rewrite(MI, Cells); } } // The rewriting could rewrite PHI nodes to non-PHI nodes, causing // regular instructions to appear in between PHI nodes. Bring all // the PHI nodes to the beginning of the block. for (auto I = B->begin(), E = B->end(); I != E; ++I) { if (I->isPHI()) continue; // I is not PHI. Find the next PHI node P. auto P = I; while (++P != E) if (P->isPHI()) break; // Not found. if (P == E) break; // Splice P right before I. B->splice(I, B, P); // Reset I to point at the just spliced PHI node. --I; } // Update the block successor information: remove unnecessary successors. if (HaveTargets) { SmallVector ToRemove; for (MachineBasicBlock *SB : B->successors()) { if (!Targets.count(SB)) ToRemove.push_back(const_cast(SB)); Targets.remove(SB); } for (unsigned i = 0, n = ToRemove.size(); i < n; ++i) removeCFGEdge(B, ToRemove[i]); // If there are any blocks left in the computed targets, it means that // we think that the block could go somewhere, but the CFG does not. // This could legitimately happen in blocks that have non-returning // calls---we would think that the execution can continue, but the // CFG will not have a successor edge. } } // Need to do some final post-processing. // If a branch was not executable, it will not get rewritten, but should // be removed (or replaced with something equivalent to a A2_nop). We can't // erase instructions during rewriting, so this needs to be delayed until // now. for (MachineBasicBlock &B : MF) { MachineBasicBlock::iterator I = B.begin(), E = B.end(); while (I != E) { auto Next = std::next(I); if (I->isBranch() && !InstrExec.count(&*I)) B.erase(I); I = Next; } } return Changed; } // This is the constant propagation algorithm as described by Wegman-Zadeck. // Most of the terminology comes from there. bool MachineConstPropagator::run(MachineFunction &MF) { LLVM_DEBUG(MF.print(dbgs() << "Starting MachineConstPropagator\n", nullptr)); MRI = &MF.getRegInfo(); Cells.clear(); EdgeExec.clear(); InstrExec.clear(); assert(FlowQ.empty()); propagate(MF); bool Changed = rewrite(MF); LLVM_DEBUG({ dbgs() << "End of MachineConstPropagator (Changed=" << Changed << ")\n"; if (Changed) MF.print(dbgs(), nullptr); }); return Changed; } // -------------------------------------------------------------------- // Machine const evaluator. bool MachineConstEvaluator::getCell(const Register &R, const CellMap &Inputs, LatticeCell &RC) { if (!TargetRegisterInfo::isVirtualRegister(R.Reg)) return false; const LatticeCell &L = Inputs.get(R.Reg); if (!R.SubReg) { RC = L; return !RC.isBottom(); } bool Eval = evaluate(R, L, RC); return Eval && !RC.isBottom(); } bool MachineConstEvaluator::constToInt(const Constant *C, APInt &Val) const { const ConstantInt *CI = dyn_cast(C); if (!CI) return false; Val = CI->getValue(); return true; } const ConstantInt *MachineConstEvaluator::intToConst(const APInt &Val) const { return ConstantInt::get(CX, Val); } bool MachineConstEvaluator::evaluateCMPrr(uint32_t Cmp, const Register &R1, const Register &R2, const CellMap &Inputs, bool &Result) { assert(Inputs.has(R1.Reg) && Inputs.has(R2.Reg)); LatticeCell LS1, LS2; if (!getCell(R1, Inputs, LS1) || !getCell(R2, Inputs, LS2)) return false; bool IsProp1 = LS1.isProperty(); bool IsProp2 = LS2.isProperty(); if (IsProp1) { uint32_t Prop1 = LS1.properties(); if (IsProp2) return evaluateCMPpp(Cmp, Prop1, LS2.properties(), Result); uint32_t NegCmp = Comparison::negate(Cmp); return evaluateCMPrp(NegCmp, R2, Prop1, Inputs, Result); } if (IsProp2) { uint32_t Prop2 = LS2.properties(); return evaluateCMPrp(Cmp, R1, Prop2, Inputs, Result); } APInt A; bool IsTrue = true, IsFalse = true; for (unsigned i = 0; i < LS2.size(); ++i) { bool Res; bool Computed = constToInt(LS2.Values[i], A) && evaluateCMPri(Cmp, R1, A, Inputs, Res); if (!Computed) return false; IsTrue &= Res; IsFalse &= !Res; } assert(!IsTrue || !IsFalse); // The actual logical value of the comparison is same as IsTrue. Result = IsTrue; // Return true if the result was proven to be true or proven to be false. return IsTrue || IsFalse; } bool MachineConstEvaluator::evaluateCMPri(uint32_t Cmp, const Register &R1, const APInt &A2, const CellMap &Inputs, bool &Result) { assert(Inputs.has(R1.Reg)); LatticeCell LS; if (!getCell(R1, Inputs, LS)) return false; if (LS.isProperty()) return evaluateCMPpi(Cmp, LS.properties(), A2, Result); APInt A; bool IsTrue = true, IsFalse = true; for (unsigned i = 0; i < LS.size(); ++i) { bool Res; bool Computed = constToInt(LS.Values[i], A) && evaluateCMPii(Cmp, A, A2, Res); if (!Computed) return false; IsTrue &= Res; IsFalse &= !Res; } assert(!IsTrue || !IsFalse); // The actual logical value of the comparison is same as IsTrue. Result = IsTrue; // Return true if the result was proven to be true or proven to be false. return IsTrue || IsFalse; } bool MachineConstEvaluator::evaluateCMPrp(uint32_t Cmp, const Register &R1, uint64_t Props2, const CellMap &Inputs, bool &Result) { assert(Inputs.has(R1.Reg)); LatticeCell LS; if (!getCell(R1, Inputs, LS)) return false; if (LS.isProperty()) return evaluateCMPpp(Cmp, LS.properties(), Props2, Result); APInt A; uint32_t NegCmp = Comparison::negate(Cmp); bool IsTrue = true, IsFalse = true; for (unsigned i = 0; i < LS.size(); ++i) { bool Res; bool Computed = constToInt(LS.Values[i], A) && evaluateCMPpi(NegCmp, Props2, A, Res); if (!Computed) return false; IsTrue &= Res; IsFalse &= !Res; } assert(!IsTrue || !IsFalse); Result = IsTrue; return IsTrue || IsFalse; } bool MachineConstEvaluator::evaluateCMPii(uint32_t Cmp, const APInt &A1, const APInt &A2, bool &Result) { // NE is a special kind of comparison (not composed of smaller properties). if (Cmp == Comparison::NE) { Result = !APInt::isSameValue(A1, A2); return true; } if (Cmp == Comparison::EQ) { Result = APInt::isSameValue(A1, A2); return true; } if (Cmp & Comparison::EQ) { if (APInt::isSameValue(A1, A2)) return (Result = true); } assert((Cmp & (Comparison::L | Comparison::G)) && "Malformed comparison"); Result = false; unsigned W1 = A1.getBitWidth(); unsigned W2 = A2.getBitWidth(); unsigned MaxW = (W1 >= W2) ? W1 : W2; if (Cmp & Comparison::U) { const APInt Zx1 = A1.zextOrSelf(MaxW); const APInt Zx2 = A2.zextOrSelf(MaxW); if (Cmp & Comparison::L) Result = Zx1.ult(Zx2); else if (Cmp & Comparison::G) Result = Zx2.ult(Zx1); return true; } // Signed comparison. const APInt Sx1 = A1.sextOrSelf(MaxW); const APInt Sx2 = A2.sextOrSelf(MaxW); if (Cmp & Comparison::L) Result = Sx1.slt(Sx2); else if (Cmp & Comparison::G) Result = Sx2.slt(Sx1); return true; } bool MachineConstEvaluator::evaluateCMPpi(uint32_t Cmp, uint32_t Props, const APInt &A2, bool &Result) { if (Props == ConstantProperties::Unknown) return false; // Should never see NaN here, but check for it for completeness. if (Props & ConstantProperties::NaN) return false; // Infinity could theoretically be compared to a number, but the // presence of infinity here would be very suspicious. If we don't // know for sure that the number is finite, bail out. if (!(Props & ConstantProperties::Finite)) return false; // Let X be a number that has properties Props. if (Cmp & Comparison::U) { // In case of unsigned comparisons, we can only compare against 0. if (A2 == 0) { // Any x!=0 will be considered >0 in an unsigned comparison. if (Props & ConstantProperties::Zero) Result = (Cmp & Comparison::EQ); else if (Props & ConstantProperties::NonZero) Result = (Cmp & Comparison::G) || (Cmp == Comparison::NE); else return false; return true; } // A2 is not zero. The only handled case is if X = 0. if (Props & ConstantProperties::Zero) { Result = (Cmp & Comparison::L) || (Cmp == Comparison::NE); return true; } return false; } // Signed comparisons are different. if (Props & ConstantProperties::Zero) { if (A2 == 0) Result = (Cmp & Comparison::EQ); else Result = (Cmp == Comparison::NE) || ((Cmp & Comparison::L) && !A2.isNegative()) || ((Cmp & Comparison::G) && A2.isNegative()); return true; } if (Props & ConstantProperties::PosOrZero) { // X >= 0 and !(A2 < 0) => cannot compare if (!A2.isNegative()) return false; // X >= 0 and A2 < 0 Result = (Cmp & Comparison::G) || (Cmp == Comparison::NE); return true; } if (Props & ConstantProperties::NegOrZero) { // X <= 0 and Src1 < 0 => cannot compare if (A2 == 0 || A2.isNegative()) return false; // X <= 0 and A2 > 0 Result = (Cmp & Comparison::L) || (Cmp == Comparison::NE); return true; } return false; } bool MachineConstEvaluator::evaluateCMPpp(uint32_t Cmp, uint32_t Props1, uint32_t Props2, bool &Result) { using P = ConstantProperties; if ((Props1 & P::NaN) && (Props2 & P::NaN)) return false; if (!(Props1 & P::Finite) || !(Props2 & P::Finite)) return false; bool Zero1 = (Props1 & P::Zero), Zero2 = (Props2 & P::Zero); bool NonZero1 = (Props1 & P::NonZero), NonZero2 = (Props2 & P::NonZero); if (Zero1 && Zero2) { Result = (Cmp & Comparison::EQ); return true; } if (Cmp == Comparison::NE) { if ((Zero1 && NonZero2) || (NonZero1 && Zero2)) return (Result = true); return false; } if (Cmp & Comparison::U) { // In unsigned comparisons, we can only compare against a known zero, // or a known non-zero. if (Zero1 && NonZero2) { Result = (Cmp & Comparison::L); return true; } if (NonZero1 && Zero2) { Result = (Cmp & Comparison::G); return true; } return false; } // Signed comparison. The comparison is not NE. bool Poz1 = (Props1 & P::PosOrZero), Poz2 = (Props2 & P::PosOrZero); bool Nez1 = (Props1 & P::NegOrZero), Nez2 = (Props2 & P::NegOrZero); if (Nez1 && Poz2) { if (NonZero1 || NonZero2) { Result = (Cmp & Comparison::L); return true; } // Either (or both) could be zero. Can only say that X <= Y. if ((Cmp & Comparison::EQ) && (Cmp & Comparison::L)) return (Result = true); } if (Poz1 && Nez2) { if (NonZero1 || NonZero2) { Result = (Cmp & Comparison::G); return true; } // Either (or both) could be zero. Can only say that X >= Y. if ((Cmp & Comparison::EQ) && (Cmp & Comparison::G)) return (Result = true); } return false; } bool MachineConstEvaluator::evaluateCOPY(const Register &R1, const CellMap &Inputs, LatticeCell &Result) { return getCell(R1, Inputs, Result); } bool MachineConstEvaluator::evaluateANDrr(const Register &R1, const Register &R2, const CellMap &Inputs, LatticeCell &Result) { assert(Inputs.has(R1.Reg) && Inputs.has(R2.Reg)); const LatticeCell &L1 = Inputs.get(R2.Reg); const LatticeCell &L2 = Inputs.get(R2.Reg); // If both sources are bottom, exit. Otherwise try to evaluate ANDri // with the non-bottom argument passed as the immediate. This is to // catch cases of ANDing with 0. if (L2.isBottom()) { if (L1.isBottom()) return false; return evaluateANDrr(R2, R1, Inputs, Result); } LatticeCell LS2; if (!evaluate(R2, L2, LS2)) return false; if (LS2.isBottom() || LS2.isProperty()) return false; APInt A; for (unsigned i = 0; i < LS2.size(); ++i) { LatticeCell RC; bool Eval = constToInt(LS2.Values[i], A) && evaluateANDri(R1, A, Inputs, RC); if (!Eval) return false; Result.meet(RC); } return !Result.isBottom(); } bool MachineConstEvaluator::evaluateANDri(const Register &R1, const APInt &A2, const CellMap &Inputs, LatticeCell &Result) { assert(Inputs.has(R1.Reg)); if (A2 == -1) return getCell(R1, Inputs, Result); if (A2 == 0) { LatticeCell RC; RC.add(intToConst(A2)); // Overwrite Result. Result = RC; return true; } LatticeCell LS1; if (!getCell(R1, Inputs, LS1)) return false; if (LS1.isBottom() || LS1.isProperty()) return false; APInt A, ResA; for (unsigned i = 0; i < LS1.size(); ++i) { bool Eval = constToInt(LS1.Values[i], A) && evaluateANDii(A, A2, ResA); if (!Eval) return false; const Constant *C = intToConst(ResA); Result.add(C); } return !Result.isBottom(); } bool MachineConstEvaluator::evaluateANDii(const APInt &A1, const APInt &A2, APInt &Result) { Result = A1 & A2; return true; } bool MachineConstEvaluator::evaluateORrr(const Register &R1, const Register &R2, const CellMap &Inputs, LatticeCell &Result) { assert(Inputs.has(R1.Reg) && Inputs.has(R2.Reg)); const LatticeCell &L1 = Inputs.get(R2.Reg); const LatticeCell &L2 = Inputs.get(R2.Reg); // If both sources are bottom, exit. Otherwise try to evaluate ORri // with the non-bottom argument passed as the immediate. This is to // catch cases of ORing with -1. if (L2.isBottom()) { if (L1.isBottom()) return false; return evaluateORrr(R2, R1, Inputs, Result); } LatticeCell LS2; if (!evaluate(R2, L2, LS2)) return false; if (LS2.isBottom() || LS2.isProperty()) return false; APInt A; for (unsigned i = 0; i < LS2.size(); ++i) { LatticeCell RC; bool Eval = constToInt(LS2.Values[i], A) && evaluateORri(R1, A, Inputs, RC); if (!Eval) return false; Result.meet(RC); } return !Result.isBottom(); } bool MachineConstEvaluator::evaluateORri(const Register &R1, const APInt &A2, const CellMap &Inputs, LatticeCell &Result) { assert(Inputs.has(R1.Reg)); if (A2 == 0) return getCell(R1, Inputs, Result); if (A2 == -1) { LatticeCell RC; RC.add(intToConst(A2)); // Overwrite Result. Result = RC; return true; } LatticeCell LS1; if (!getCell(R1, Inputs, LS1)) return false; if (LS1.isBottom() || LS1.isProperty()) return false; APInt A, ResA; for (unsigned i = 0; i < LS1.size(); ++i) { bool Eval = constToInt(LS1.Values[i], A) && evaluateORii(A, A2, ResA); if (!Eval) return false; const Constant *C = intToConst(ResA); Result.add(C); } return !Result.isBottom(); } bool MachineConstEvaluator::evaluateORii(const APInt &A1, const APInt &A2, APInt &Result) { Result = A1 | A2; return true; } bool MachineConstEvaluator::evaluateXORrr(const Register &R1, const Register &R2, const CellMap &Inputs, LatticeCell &Result) { assert(Inputs.has(R1.Reg) && Inputs.has(R2.Reg)); LatticeCell LS1, LS2; if (!getCell(R1, Inputs, LS1) || !getCell(R2, Inputs, LS2)) return false; if (LS1.isProperty()) { if (LS1.properties() & ConstantProperties::Zero) return !(Result = LS2).isBottom(); return false; } if (LS2.isProperty()) { if (LS2.properties() & ConstantProperties::Zero) return !(Result = LS1).isBottom(); return false; } APInt A; for (unsigned i = 0; i < LS2.size(); ++i) { LatticeCell RC; bool Eval = constToInt(LS2.Values[i], A) && evaluateXORri(R1, A, Inputs, RC); if (!Eval) return false; Result.meet(RC); } return !Result.isBottom(); } bool MachineConstEvaluator::evaluateXORri(const Register &R1, const APInt &A2, const CellMap &Inputs, LatticeCell &Result) { assert(Inputs.has(R1.Reg)); LatticeCell LS1; if (!getCell(R1, Inputs, LS1)) return false; if (LS1.isProperty()) { if (LS1.properties() & ConstantProperties::Zero) { const Constant *C = intToConst(A2); Result.add(C); return !Result.isBottom(); } return false; } APInt A, XA; for (unsigned i = 0; i < LS1.size(); ++i) { bool Eval = constToInt(LS1.Values[i], A) && evaluateXORii(A, A2, XA); if (!Eval) return false; const Constant *C = intToConst(XA); Result.add(C); } return !Result.isBottom(); } bool MachineConstEvaluator::evaluateXORii(const APInt &A1, const APInt &A2, APInt &Result) { Result = A1 ^ A2; return true; } bool MachineConstEvaluator::evaluateZEXTr(const Register &R1, unsigned Width, unsigned Bits, const CellMap &Inputs, LatticeCell &Result) { assert(Inputs.has(R1.Reg)); LatticeCell LS1; if (!getCell(R1, Inputs, LS1)) return false; if (LS1.isProperty()) return false; APInt A, XA; for (unsigned i = 0; i < LS1.size(); ++i) { bool Eval = constToInt(LS1.Values[i], A) && evaluateZEXTi(A, Width, Bits, XA); if (!Eval) return false; const Constant *C = intToConst(XA); Result.add(C); } return true; } bool MachineConstEvaluator::evaluateZEXTi(const APInt &A1, unsigned Width, unsigned Bits, APInt &Result) { unsigned BW = A1.getBitWidth(); (void)BW; assert(Width >= Bits && BW >= Bits); APInt Mask = APInt::getLowBitsSet(Width, Bits); Result = A1.zextOrTrunc(Width) & Mask; return true; } bool MachineConstEvaluator::evaluateSEXTr(const Register &R1, unsigned Width, unsigned Bits, const CellMap &Inputs, LatticeCell &Result) { assert(Inputs.has(R1.Reg)); LatticeCell LS1; if (!getCell(R1, Inputs, LS1)) return false; if (LS1.isBottom() || LS1.isProperty()) return false; APInt A, XA; for (unsigned i = 0; i < LS1.size(); ++i) { bool Eval = constToInt(LS1.Values[i], A) && evaluateSEXTi(A, Width, Bits, XA); if (!Eval) return false; const Constant *C = intToConst(XA); Result.add(C); } return true; } bool MachineConstEvaluator::evaluateSEXTi(const APInt &A1, unsigned Width, unsigned Bits, APInt &Result) { unsigned BW = A1.getBitWidth(); assert(Width >= Bits && BW >= Bits); // Special case to make things faster for smaller source widths. // Sign extension of 0 bits generates 0 as a result. This is consistent // with what the HW does. if (Bits == 0) { Result = APInt(Width, 0); return true; } // In C, shifts by 64 invoke undefined behavior: handle that case in APInt. if (BW <= 64 && Bits != 0) { int64_t V = A1.getSExtValue(); switch (Bits) { case 8: V = static_cast(V); break; case 16: V = static_cast(V); break; case 32: V = static_cast(V); break; default: // Shift left to lose all bits except lower "Bits" bits, then shift // the value back, replicating what was a sign bit after the first // shift. V = (V << (64-Bits)) >> (64-Bits); break; } // V is a 64-bit sign-extended value. Convert it to APInt of desired // width. Result = APInt(Width, V, true); return true; } // Slow case: the value doesn't fit in int64_t. if (Bits < BW) Result = A1.trunc(Bits).sext(Width); else // Bits == BW Result = A1.sext(Width); return true; } bool MachineConstEvaluator::evaluateCLBr(const Register &R1, bool Zeros, bool Ones, const CellMap &Inputs, LatticeCell &Result) { assert(Inputs.has(R1.Reg)); LatticeCell LS1; if (!getCell(R1, Inputs, LS1)) return false; if (LS1.isBottom() || LS1.isProperty()) return false; APInt A, CA; for (unsigned i = 0; i < LS1.size(); ++i) { bool Eval = constToInt(LS1.Values[i], A) && evaluateCLBi(A, Zeros, Ones, CA); if (!Eval) return false; const Constant *C = intToConst(CA); Result.add(C); } return true; } bool MachineConstEvaluator::evaluateCLBi(const APInt &A1, bool Zeros, bool Ones, APInt &Result) { unsigned BW = A1.getBitWidth(); if (!Zeros && !Ones) return false; unsigned Count = 0; if (Zeros && (Count == 0)) Count = A1.countLeadingZeros(); if (Ones && (Count == 0)) Count = A1.countLeadingOnes(); Result = APInt(BW, static_cast(Count), false); return true; } bool MachineConstEvaluator::evaluateCTBr(const Register &R1, bool Zeros, bool Ones, const CellMap &Inputs, LatticeCell &Result) { assert(Inputs.has(R1.Reg)); LatticeCell LS1; if (!getCell(R1, Inputs, LS1)) return false; if (LS1.isBottom() || LS1.isProperty()) return false; APInt A, CA; for (unsigned i = 0; i < LS1.size(); ++i) { bool Eval = constToInt(LS1.Values[i], A) && evaluateCTBi(A, Zeros, Ones, CA); if (!Eval) return false; const Constant *C = intToConst(CA); Result.add(C); } return true; } bool MachineConstEvaluator::evaluateCTBi(const APInt &A1, bool Zeros, bool Ones, APInt &Result) { unsigned BW = A1.getBitWidth(); if (!Zeros && !Ones) return false; unsigned Count = 0; if (Zeros && (Count == 0)) Count = A1.countTrailingZeros(); if (Ones && (Count == 0)) Count = A1.countTrailingOnes(); Result = APInt(BW, static_cast(Count), false); return true; } bool MachineConstEvaluator::evaluateEXTRACTr(const Register &R1, unsigned Width, unsigned Bits, unsigned Offset, bool Signed, const CellMap &Inputs, LatticeCell &Result) { assert(Inputs.has(R1.Reg)); assert(Bits+Offset <= Width); LatticeCell LS1; if (!getCell(R1, Inputs, LS1)) return false; if (LS1.isBottom()) return false; if (LS1.isProperty()) { uint32_t Ps = LS1.properties(); if (Ps & ConstantProperties::Zero) { const Constant *C = intToConst(APInt(Width, 0, false)); Result.add(C); return true; } return false; } APInt A, CA; for (unsigned i = 0; i < LS1.size(); ++i) { bool Eval = constToInt(LS1.Values[i], A) && evaluateEXTRACTi(A, Bits, Offset, Signed, CA); if (!Eval) return false; const Constant *C = intToConst(CA); Result.add(C); } return true; } bool MachineConstEvaluator::evaluateEXTRACTi(const APInt &A1, unsigned Bits, unsigned Offset, bool Signed, APInt &Result) { unsigned BW = A1.getBitWidth(); assert(Bits+Offset <= BW); // Extracting 0 bits generates 0 as a result (as indicated by the HW people). if (Bits == 0) { Result = APInt(BW, 0); return true; } if (BW <= 64) { int64_t V = A1.getZExtValue(); V <<= (64-Bits-Offset); if (Signed) V >>= (64-Bits); else V = static_cast(V) >> (64-Bits); Result = APInt(BW, V, Signed); return true; } if (Signed) Result = A1.shl(BW-Bits-Offset).ashr(BW-Bits); else Result = A1.shl(BW-Bits-Offset).lshr(BW-Bits); return true; } bool MachineConstEvaluator::evaluateSplatr(const Register &R1, unsigned Bits, unsigned Count, const CellMap &Inputs, LatticeCell &Result) { assert(Inputs.has(R1.Reg)); LatticeCell LS1; if (!getCell(R1, Inputs, LS1)) return false; if (LS1.isBottom() || LS1.isProperty()) return false; APInt A, SA; for (unsigned i = 0; i < LS1.size(); ++i) { bool Eval = constToInt(LS1.Values[i], A) && evaluateSplati(A, Bits, Count, SA); if (!Eval) return false; const Constant *C = intToConst(SA); Result.add(C); } return true; } bool MachineConstEvaluator::evaluateSplati(const APInt &A1, unsigned Bits, unsigned Count, APInt &Result) { assert(Count > 0); unsigned BW = A1.getBitWidth(), SW = Count*Bits; APInt LoBits = (Bits < BW) ? A1.trunc(Bits) : A1.zextOrSelf(Bits); if (Count > 1) LoBits = LoBits.zext(SW); APInt Res(SW, 0, false); for (unsigned i = 0; i < Count; ++i) { Res <<= Bits; Res |= LoBits; } Result = Res; return true; } // ---------------------------------------------------------------------- // Hexagon-specific code. namespace llvm { FunctionPass *createHexagonConstPropagationPass(); void initializeHexagonConstPropagationPass(PassRegistry &Registry); } // end namespace llvm namespace { class HexagonConstEvaluator : public MachineConstEvaluator { public: HexagonConstEvaluator(MachineFunction &Fn); bool evaluate(const MachineInstr &MI, const CellMap &Inputs, CellMap &Outputs) override; bool evaluate(const Register &R, const LatticeCell &SrcC, LatticeCell &Result) override; bool evaluate(const MachineInstr &BrI, const CellMap &Inputs, SetVector &Targets, bool &FallsThru) override; bool rewrite(MachineInstr &MI, const CellMap &Inputs) override; private: unsigned getRegBitWidth(unsigned Reg) const; static uint32_t getCmp(unsigned Opc); static APInt getCmpImm(unsigned Opc, unsigned OpX, const MachineOperand &MO); void replaceWithNop(MachineInstr &MI); bool evaluateHexRSEQ32(Register RL, Register RH, const CellMap &Inputs, LatticeCell &Result); bool evaluateHexCompare(const MachineInstr &MI, const CellMap &Inputs, CellMap &Outputs); // This is suitable to be called for compare-and-jump instructions. bool evaluateHexCompare2(uint32_t Cmp, const MachineOperand &Src1, const MachineOperand &Src2, const CellMap &Inputs, bool &Result); bool evaluateHexLogical(const MachineInstr &MI, const CellMap &Inputs, CellMap &Outputs); bool evaluateHexCondMove(const MachineInstr &MI, const CellMap &Inputs, CellMap &Outputs); bool evaluateHexExt(const MachineInstr &MI, const CellMap &Inputs, CellMap &Outputs); bool evaluateHexVector1(const MachineInstr &MI, const CellMap &Inputs, CellMap &Outputs); bool evaluateHexVector2(const MachineInstr &MI, const CellMap &Inputs, CellMap &Outputs); void replaceAllRegUsesWith(unsigned FromReg, unsigned ToReg); bool rewriteHexBranch(MachineInstr &BrI, const CellMap &Inputs); bool rewriteHexConstDefs(MachineInstr &MI, const CellMap &Inputs, bool &AllDefs); bool rewriteHexConstUses(MachineInstr &MI, const CellMap &Inputs); MachineRegisterInfo *MRI; const HexagonInstrInfo &HII; const HexagonRegisterInfo &HRI; }; class HexagonConstPropagation : public MachineFunctionPass { public: static char ID; HexagonConstPropagation() : MachineFunctionPass(ID) {} StringRef getPassName() const override { return "Hexagon Constant Propagation"; } bool runOnMachineFunction(MachineFunction &MF) override { const Function &F = MF.getFunction(); if (skipFunction(F)) return false; HexagonConstEvaluator HCE(MF); return MachineConstPropagator(HCE).run(MF); } }; } // end anonymous namespace char HexagonConstPropagation::ID = 0; INITIALIZE_PASS(HexagonConstPropagation, "hexagon-constp", "Hexagon Constant Propagation", false, false) HexagonConstEvaluator::HexagonConstEvaluator(MachineFunction &Fn) : MachineConstEvaluator(Fn), HII(*Fn.getSubtarget().getInstrInfo()), HRI(*Fn.getSubtarget().getRegisterInfo()) { MRI = &Fn.getRegInfo(); } bool HexagonConstEvaluator::evaluate(const MachineInstr &MI, const CellMap &Inputs, CellMap &Outputs) { if (MI.isCall()) return false; if (MI.getNumOperands() == 0 || !MI.getOperand(0).isReg()) return false; const MachineOperand &MD = MI.getOperand(0); if (!MD.isDef()) return false; unsigned Opc = MI.getOpcode(); Register DefR(MD); assert(!DefR.SubReg); if (!TargetRegisterInfo::isVirtualRegister(DefR.Reg)) return false; if (MI.isCopy()) { LatticeCell RC; Register SrcR(MI.getOperand(1)); bool Eval = evaluateCOPY(SrcR, Inputs, RC); if (!Eval) return false; Outputs.update(DefR.Reg, RC); return true; } if (MI.isRegSequence()) { unsigned Sub1 = MI.getOperand(2).getImm(); unsigned Sub2 = MI.getOperand(4).getImm(); const TargetRegisterClass &DefRC = *MRI->getRegClass(DefR.Reg); unsigned SubLo = HRI.getHexagonSubRegIndex(DefRC, Hexagon::ps_sub_lo); unsigned SubHi = HRI.getHexagonSubRegIndex(DefRC, Hexagon::ps_sub_hi); if (Sub1 != SubLo && Sub1 != SubHi) return false; if (Sub2 != SubLo && Sub2 != SubHi) return false; assert(Sub1 != Sub2); bool LoIs1 = (Sub1 == SubLo); const MachineOperand &OpLo = LoIs1 ? MI.getOperand(1) : MI.getOperand(3); const MachineOperand &OpHi = LoIs1 ? MI.getOperand(3) : MI.getOperand(1); LatticeCell RC; Register SrcRL(OpLo), SrcRH(OpHi); bool Eval = evaluateHexRSEQ32(SrcRL, SrcRH, Inputs, RC); if (!Eval) return false; Outputs.update(DefR.Reg, RC); return true; } if (MI.isCompare()) { bool Eval = evaluateHexCompare(MI, Inputs, Outputs); return Eval; } switch (Opc) { default: return false; case Hexagon::A2_tfrsi: case Hexagon::A2_tfrpi: case Hexagon::CONST32: case Hexagon::CONST64: { const MachineOperand &VO = MI.getOperand(1); // The operand of CONST32 can be a blockaddress, e.g. // %0 = CONST32 // Do this check for all instructions for safety. if (!VO.isImm()) return false; int64_t V = MI.getOperand(1).getImm(); unsigned W = getRegBitWidth(DefR.Reg); if (W != 32 && W != 64) return false; IntegerType *Ty = (W == 32) ? Type::getInt32Ty(CX) : Type::getInt64Ty(CX); const ConstantInt *CI = ConstantInt::get(Ty, V, true); LatticeCell RC = Outputs.get(DefR.Reg); RC.add(CI); Outputs.update(DefR.Reg, RC); break; } case Hexagon::PS_true: case Hexagon::PS_false: { LatticeCell RC = Outputs.get(DefR.Reg); bool NonZero = (Opc == Hexagon::PS_true); uint32_t P = NonZero ? ConstantProperties::NonZero : ConstantProperties::Zero; RC.add(P); Outputs.update(DefR.Reg, RC); break; } case Hexagon::A2_and: case Hexagon::A2_andir: case Hexagon::A2_andp: case Hexagon::A2_or: case Hexagon::A2_orir: case Hexagon::A2_orp: case Hexagon::A2_xor: case Hexagon::A2_xorp: { bool Eval = evaluateHexLogical(MI, Inputs, Outputs); if (!Eval) return false; break; } case Hexagon::A2_combineii: // combine(#s8Ext, #s8) case Hexagon::A4_combineii: // combine(#s8, #u6Ext) { if (!MI.getOperand(1).isImm() || !MI.getOperand(2).isImm()) return false; uint64_t Hi = MI.getOperand(1).getImm(); uint64_t Lo = MI.getOperand(2).getImm(); uint64_t Res = (Hi << 32) | (Lo & 0xFFFFFFFF); IntegerType *Ty = Type::getInt64Ty(CX); const ConstantInt *CI = ConstantInt::get(Ty, Res, false); LatticeCell RC = Outputs.get(DefR.Reg); RC.add(CI); Outputs.update(DefR.Reg, RC); break; } case Hexagon::S2_setbit_i: { int64_t B = MI.getOperand(2).getImm(); assert(B >=0 && B < 32); APInt A(32, (1ull << B), false); Register R(MI.getOperand(1)); LatticeCell RC = Outputs.get(DefR.Reg); bool Eval = evaluateORri(R, A, Inputs, RC); if (!Eval) return false; Outputs.update(DefR.Reg, RC); break; } case Hexagon::C2_mux: case Hexagon::C2_muxir: case Hexagon::C2_muxri: case Hexagon::C2_muxii: { bool Eval = evaluateHexCondMove(MI, Inputs, Outputs); if (!Eval) return false; break; } case Hexagon::A2_sxtb: case Hexagon::A2_sxth: case Hexagon::A2_sxtw: case Hexagon::A2_zxtb: case Hexagon::A2_zxth: { bool Eval = evaluateHexExt(MI, Inputs, Outputs); if (!Eval) return false; break; } case Hexagon::S2_ct0: case Hexagon::S2_ct0p: case Hexagon::S2_ct1: case Hexagon::S2_ct1p: { using namespace Hexagon; bool Ones = (Opc == S2_ct1) || (Opc == S2_ct1p); Register R1(MI.getOperand(1)); assert(Inputs.has(R1.Reg)); LatticeCell T; bool Eval = evaluateCTBr(R1, !Ones, Ones, Inputs, T); if (!Eval) return false; // All of these instructions return a 32-bit value. The evaluate // will generate the same type as the operand, so truncate the // result if necessary. APInt C; LatticeCell RC = Outputs.get(DefR.Reg); for (unsigned i = 0; i < T.size(); ++i) { const Constant *CI = T.Values[i]; if (constToInt(CI, C) && C.getBitWidth() > 32) CI = intToConst(C.trunc(32)); RC.add(CI); } Outputs.update(DefR.Reg, RC); break; } case Hexagon::S2_cl0: case Hexagon::S2_cl0p: case Hexagon::S2_cl1: case Hexagon::S2_cl1p: case Hexagon::S2_clb: case Hexagon::S2_clbp: { using namespace Hexagon; bool OnlyZeros = (Opc == S2_cl0) || (Opc == S2_cl0p); bool OnlyOnes = (Opc == S2_cl1) || (Opc == S2_cl1p); Register R1(MI.getOperand(1)); assert(Inputs.has(R1.Reg)); LatticeCell T; bool Eval = evaluateCLBr(R1, !OnlyOnes, !OnlyZeros, Inputs, T); if (!Eval) return false; // All of these instructions return a 32-bit value. The evaluate // will generate the same type as the operand, so truncate the // result if necessary. APInt C; LatticeCell RC = Outputs.get(DefR.Reg); for (unsigned i = 0; i < T.size(); ++i) { const Constant *CI = T.Values[i]; if (constToInt(CI, C) && C.getBitWidth() > 32) CI = intToConst(C.trunc(32)); RC.add(CI); } Outputs.update(DefR.Reg, RC); break; } case Hexagon::S4_extract: case Hexagon::S4_extractp: case Hexagon::S2_extractu: case Hexagon::S2_extractup: { bool Signed = (Opc == Hexagon::S4_extract) || (Opc == Hexagon::S4_extractp); Register R1(MI.getOperand(1)); unsigned BW = getRegBitWidth(R1.Reg); unsigned Bits = MI.getOperand(2).getImm(); unsigned Offset = MI.getOperand(3).getImm(); LatticeCell RC = Outputs.get(DefR.Reg); if (Offset >= BW) { APInt Zero(BW, 0, false); RC.add(intToConst(Zero)); break; } if (Offset+Bits > BW) { // If the requested bitfield extends beyond the most significant bit, // the extra bits are treated as 0s. To emulate this behavior, reduce // the number of requested bits, and make the extract unsigned. Bits = BW-Offset; Signed = false; } bool Eval = evaluateEXTRACTr(R1, BW, Bits, Offset, Signed, Inputs, RC); if (!Eval) return false; Outputs.update(DefR.Reg, RC); break; } case Hexagon::S2_vsplatrb: case Hexagon::S2_vsplatrh: // vabsh, vabsh:sat // vabsw, vabsw:sat // vconj:sat // vrndwh, vrndwh:sat // vsathb, vsathub, vsatwuh // vsxtbh, vsxthw // vtrunehb, vtrunohb // vzxtbh, vzxthw { bool Eval = evaluateHexVector1(MI, Inputs, Outputs); if (!Eval) return false; break; } // TODO: // A2_vaddh // A2_vaddhs // A2_vaddw // A2_vaddws } return true; } bool HexagonConstEvaluator::evaluate(const Register &R, const LatticeCell &Input, LatticeCell &Result) { if (!R.SubReg) { Result = Input; return true; } const TargetRegisterClass *RC = MRI->getRegClass(R.Reg); if (RC != &Hexagon::DoubleRegsRegClass) return false; if (R.SubReg != Hexagon::isub_lo && R.SubReg != Hexagon::isub_hi) return false; assert(!Input.isTop()); if (Input.isBottom()) return false; using P = ConstantProperties; if (Input.isProperty()) { uint32_t Ps = Input.properties(); if (Ps & (P::Zero|P::NaN)) { uint32_t Ns = (Ps & (P::Zero|P::NaN|P::SignProperties)); Result.add(Ns); return true; } if (R.SubReg == Hexagon::isub_hi) { uint32_t Ns = (Ps & P::SignProperties); Result.add(Ns); return true; } return false; } // The Input cell contains some known values. Pick the word corresponding // to the subregister. APInt A; for (unsigned i = 0; i < Input.size(); ++i) { const Constant *C = Input.Values[i]; if (!constToInt(C, A)) return false; if (!A.isIntN(64)) return false; uint64_t U = A.getZExtValue(); if (R.SubReg == Hexagon::isub_hi) U >>= 32; U &= 0xFFFFFFFFULL; uint32_t U32 = Lo_32(U); int32_t V32; memcpy(&V32, &U32, sizeof V32); IntegerType *Ty = Type::getInt32Ty(CX); const ConstantInt *C32 = ConstantInt::get(Ty, static_cast(V32)); Result.add(C32); } return true; } bool HexagonConstEvaluator::evaluate(const MachineInstr &BrI, const CellMap &Inputs, SetVector &Targets, bool &FallsThru) { // We need to evaluate one branch at a time. TII::analyzeBranch checks // all the branches in a basic block at once, so we cannot use it. unsigned Opc = BrI.getOpcode(); bool SimpleBranch = false; bool Negated = false; switch (Opc) { case Hexagon::J2_jumpf: case Hexagon::J2_jumpfnew: case Hexagon::J2_jumpfnewpt: Negated = true; LLVM_FALLTHROUGH; case Hexagon::J2_jumpt: case Hexagon::J2_jumptnew: case Hexagon::J2_jumptnewpt: // Simple branch: if([!]Pn) jump ... // i.e. Op0 = predicate, Op1 = branch target. SimpleBranch = true; break; case Hexagon::J2_jump: Targets.insert(BrI.getOperand(0).getMBB()); FallsThru = false; return true; default: Undetermined: // If the branch is of unknown type, assume that all successors are // executable. FallsThru = !BrI.isUnconditionalBranch(); return false; } if (SimpleBranch) { const MachineOperand &MD = BrI.getOperand(0); Register PR(MD); // If the condition operand has a subregister, this is not something // we currently recognize. if (PR.SubReg) goto Undetermined; assert(Inputs.has(PR.Reg)); const LatticeCell &PredC = Inputs.get(PR.Reg); if (PredC.isBottom()) goto Undetermined; uint32_t Props = PredC.properties(); bool CTrue = false, CFalse = false; if (Props & ConstantProperties::Zero) CFalse = true; else if (Props & ConstantProperties::NonZero) CTrue = true; // If the condition is not known to be either, bail out. if (!CTrue && !CFalse) goto Undetermined; const MachineBasicBlock *BranchTarget = BrI.getOperand(1).getMBB(); FallsThru = false; if ((!Negated && CTrue) || (Negated && CFalse)) Targets.insert(BranchTarget); else if ((!Negated && CFalse) || (Negated && CTrue)) FallsThru = true; else goto Undetermined; } return true; } bool HexagonConstEvaluator::rewrite(MachineInstr &MI, const CellMap &Inputs) { if (MI.isBranch()) return rewriteHexBranch(MI, Inputs); unsigned Opc = MI.getOpcode(); switch (Opc) { default: break; case Hexagon::A2_tfrsi: case Hexagon::A2_tfrpi: case Hexagon::CONST32: case Hexagon::CONST64: case Hexagon::PS_true: case Hexagon::PS_false: return false; } unsigned NumOp = MI.getNumOperands(); if (NumOp == 0) return false; bool AllDefs, Changed; Changed = rewriteHexConstDefs(MI, Inputs, AllDefs); // If not all defs have been rewritten (i.e. the instruction defines // a register that is not compile-time constant), then try to rewrite // register operands that are known to be constant with immediates. if (!AllDefs) Changed |= rewriteHexConstUses(MI, Inputs); return Changed; } unsigned HexagonConstEvaluator::getRegBitWidth(unsigned Reg) const { const TargetRegisterClass *RC = MRI->getRegClass(Reg); if (Hexagon::IntRegsRegClass.hasSubClassEq(RC)) return 32; if (Hexagon::DoubleRegsRegClass.hasSubClassEq(RC)) return 64; if (Hexagon::PredRegsRegClass.hasSubClassEq(RC)) return 8; llvm_unreachable("Invalid register"); return 0; } uint32_t HexagonConstEvaluator::getCmp(unsigned Opc) { switch (Opc) { case Hexagon::C2_cmpeq: case Hexagon::C2_cmpeqp: case Hexagon::A4_cmpbeq: case Hexagon::A4_cmpheq: case Hexagon::A4_cmpbeqi: case Hexagon::A4_cmpheqi: case Hexagon::C2_cmpeqi: case Hexagon::J4_cmpeqn1_t_jumpnv_nt: case Hexagon::J4_cmpeqn1_t_jumpnv_t: case Hexagon::J4_cmpeqi_t_jumpnv_nt: case Hexagon::J4_cmpeqi_t_jumpnv_t: case Hexagon::J4_cmpeq_t_jumpnv_nt: case Hexagon::J4_cmpeq_t_jumpnv_t: return Comparison::EQ; case Hexagon::C4_cmpneq: case Hexagon::C4_cmpneqi: case Hexagon::J4_cmpeqn1_f_jumpnv_nt: case Hexagon::J4_cmpeqn1_f_jumpnv_t: case Hexagon::J4_cmpeqi_f_jumpnv_nt: case Hexagon::J4_cmpeqi_f_jumpnv_t: case Hexagon::J4_cmpeq_f_jumpnv_nt: case Hexagon::J4_cmpeq_f_jumpnv_t: return Comparison::NE; case Hexagon::C2_cmpgt: case Hexagon::C2_cmpgtp: case Hexagon::A4_cmpbgt: case Hexagon::A4_cmphgt: case Hexagon::A4_cmpbgti: case Hexagon::A4_cmphgti: case Hexagon::C2_cmpgti: case Hexagon::J4_cmpgtn1_t_jumpnv_nt: case Hexagon::J4_cmpgtn1_t_jumpnv_t: case Hexagon::J4_cmpgti_t_jumpnv_nt: case Hexagon::J4_cmpgti_t_jumpnv_t: case Hexagon::J4_cmpgt_t_jumpnv_nt: case Hexagon::J4_cmpgt_t_jumpnv_t: return Comparison::GTs; case Hexagon::C4_cmplte: case Hexagon::C4_cmpltei: case Hexagon::J4_cmpgtn1_f_jumpnv_nt: case Hexagon::J4_cmpgtn1_f_jumpnv_t: case Hexagon::J4_cmpgti_f_jumpnv_nt: case Hexagon::J4_cmpgti_f_jumpnv_t: case Hexagon::J4_cmpgt_f_jumpnv_nt: case Hexagon::J4_cmpgt_f_jumpnv_t: return Comparison::LEs; case Hexagon::C2_cmpgtu: case Hexagon::C2_cmpgtup: case Hexagon::A4_cmpbgtu: case Hexagon::A4_cmpbgtui: case Hexagon::A4_cmphgtu: case Hexagon::A4_cmphgtui: case Hexagon::C2_cmpgtui: case Hexagon::J4_cmpgtui_t_jumpnv_nt: case Hexagon::J4_cmpgtui_t_jumpnv_t: case Hexagon::J4_cmpgtu_t_jumpnv_nt: case Hexagon::J4_cmpgtu_t_jumpnv_t: return Comparison::GTu; case Hexagon::J4_cmpltu_f_jumpnv_nt: case Hexagon::J4_cmpltu_f_jumpnv_t: return Comparison::GEu; case Hexagon::J4_cmpltu_t_jumpnv_nt: case Hexagon::J4_cmpltu_t_jumpnv_t: return Comparison::LTu; case Hexagon::J4_cmplt_f_jumpnv_nt: case Hexagon::J4_cmplt_f_jumpnv_t: return Comparison::GEs; case Hexagon::C4_cmplteu: case Hexagon::C4_cmplteui: case Hexagon::J4_cmpgtui_f_jumpnv_nt: case Hexagon::J4_cmpgtui_f_jumpnv_t: case Hexagon::J4_cmpgtu_f_jumpnv_nt: case Hexagon::J4_cmpgtu_f_jumpnv_t: return Comparison::LEu; case Hexagon::J4_cmplt_t_jumpnv_nt: case Hexagon::J4_cmplt_t_jumpnv_t: return Comparison::LTs; default: break; } return Comparison::Unk; } APInt HexagonConstEvaluator::getCmpImm(unsigned Opc, unsigned OpX, const MachineOperand &MO) { bool Signed = false; switch (Opc) { case Hexagon::A4_cmpbgtui: // u7 case Hexagon::A4_cmphgtui: // u7 break; case Hexagon::A4_cmpheqi: // s8 case Hexagon::C4_cmpneqi: // s8 Signed = true; break; case Hexagon::A4_cmpbeqi: // u8 break; case Hexagon::C2_cmpgtui: // u9 case Hexagon::C4_cmplteui: // u9 break; case Hexagon::C2_cmpeqi: // s10 case Hexagon::C2_cmpgti: // s10 case Hexagon::C4_cmpltei: // s10 Signed = true; break; case Hexagon::J4_cmpeqi_f_jumpnv_nt: // u5 case Hexagon::J4_cmpeqi_f_jumpnv_t: // u5 case Hexagon::J4_cmpeqi_t_jumpnv_nt: // u5 case Hexagon::J4_cmpeqi_t_jumpnv_t: // u5 case Hexagon::J4_cmpgti_f_jumpnv_nt: // u5 case Hexagon::J4_cmpgti_f_jumpnv_t: // u5 case Hexagon::J4_cmpgti_t_jumpnv_nt: // u5 case Hexagon::J4_cmpgti_t_jumpnv_t: // u5 case Hexagon::J4_cmpgtui_f_jumpnv_nt: // u5 case Hexagon::J4_cmpgtui_f_jumpnv_t: // u5 case Hexagon::J4_cmpgtui_t_jumpnv_nt: // u5 case Hexagon::J4_cmpgtui_t_jumpnv_t: // u5 break; default: llvm_unreachable("Unhandled instruction"); break; } uint64_t Val = MO.getImm(); return APInt(32, Val, Signed); } void HexagonConstEvaluator::replaceWithNop(MachineInstr &MI) { MI.setDesc(HII.get(Hexagon::A2_nop)); while (MI.getNumOperands() > 0) MI.RemoveOperand(0); } bool HexagonConstEvaluator::evaluateHexRSEQ32(Register RL, Register RH, const CellMap &Inputs, LatticeCell &Result) { assert(Inputs.has(RL.Reg) && Inputs.has(RH.Reg)); LatticeCell LSL, LSH; if (!getCell(RL, Inputs, LSL) || !getCell(RH, Inputs, LSH)) return false; if (LSL.isProperty() || LSH.isProperty()) return false; unsigned LN = LSL.size(), HN = LSH.size(); SmallVector LoVs(LN), HiVs(HN); for (unsigned i = 0; i < LN; ++i) { bool Eval = constToInt(LSL.Values[i], LoVs[i]); if (!Eval) return false; assert(LoVs[i].getBitWidth() == 32); } for (unsigned i = 0; i < HN; ++i) { bool Eval = constToInt(LSH.Values[i], HiVs[i]); if (!Eval) return false; assert(HiVs[i].getBitWidth() == 32); } for (unsigned i = 0; i < HiVs.size(); ++i) { APInt HV = HiVs[i].zextOrSelf(64) << 32; for (unsigned j = 0; j < LoVs.size(); ++j) { APInt LV = LoVs[j].zextOrSelf(64); const Constant *C = intToConst(HV | LV); Result.add(C); if (Result.isBottom()) return false; } } return !Result.isBottom(); } bool HexagonConstEvaluator::evaluateHexCompare(const MachineInstr &MI, const CellMap &Inputs, CellMap &Outputs) { unsigned Opc = MI.getOpcode(); bool Classic = false; switch (Opc) { case Hexagon::C2_cmpeq: case Hexagon::C2_cmpeqp: case Hexagon::C2_cmpgt: case Hexagon::C2_cmpgtp: case Hexagon::C2_cmpgtu: case Hexagon::C2_cmpgtup: case Hexagon::C2_cmpeqi: case Hexagon::C2_cmpgti: case Hexagon::C2_cmpgtui: // Classic compare: Dst0 = CMP Src1, Src2 Classic = true; break; default: // Not handling other compare instructions now. return false; } if (Classic) { const MachineOperand &Src1 = MI.getOperand(1); const MachineOperand &Src2 = MI.getOperand(2); bool Result; unsigned Opc = MI.getOpcode(); bool Computed = evaluateHexCompare2(Opc, Src1, Src2, Inputs, Result); if (Computed) { // Only create a zero/non-zero cell. At this time there isn't really // much need for specific values. Register DefR(MI.getOperand(0)); LatticeCell L = Outputs.get(DefR.Reg); uint32_t P = Result ? ConstantProperties::NonZero : ConstantProperties::Zero; L.add(P); Outputs.update(DefR.Reg, L); return true; } } return false; } bool HexagonConstEvaluator::evaluateHexCompare2(unsigned Opc, const MachineOperand &Src1, const MachineOperand &Src2, const CellMap &Inputs, bool &Result) { uint32_t Cmp = getCmp(Opc); bool Reg1 = Src1.isReg(), Reg2 = Src2.isReg(); bool Imm1 = Src1.isImm(), Imm2 = Src2.isImm(); if (Reg1) { Register R1(Src1); if (Reg2) { Register R2(Src2); return evaluateCMPrr(Cmp, R1, R2, Inputs, Result); } else if (Imm2) { APInt A2 = getCmpImm(Opc, 2, Src2); return evaluateCMPri(Cmp, R1, A2, Inputs, Result); } } else if (Imm1) { APInt A1 = getCmpImm(Opc, 1, Src1); if (Reg2) { Register R2(Src2); uint32_t NegCmp = Comparison::negate(Cmp); return evaluateCMPri(NegCmp, R2, A1, Inputs, Result); } else if (Imm2) { APInt A2 = getCmpImm(Opc, 2, Src2); return evaluateCMPii(Cmp, A1, A2, Result); } } // Unknown kind of comparison. return false; } bool HexagonConstEvaluator::evaluateHexLogical(const MachineInstr &MI, const CellMap &Inputs, CellMap &Outputs) { unsigned Opc = MI.getOpcode(); if (MI.getNumOperands() != 3) return false; const MachineOperand &Src1 = MI.getOperand(1); const MachineOperand &Src2 = MI.getOperand(2); Register R1(Src1); bool Eval = false; LatticeCell RC; switch (Opc) { default: return false; case Hexagon::A2_and: case Hexagon::A2_andp: Eval = evaluateANDrr(R1, Register(Src2), Inputs, RC); break; case Hexagon::A2_andir: { if (!Src2.isImm()) return false; APInt A(32, Src2.getImm(), true); Eval = evaluateANDri(R1, A, Inputs, RC); break; } case Hexagon::A2_or: case Hexagon::A2_orp: Eval = evaluateORrr(R1, Register(Src2), Inputs, RC); break; case Hexagon::A2_orir: { if (!Src2.isImm()) return false; APInt A(32, Src2.getImm(), true); Eval = evaluateORri(R1, A, Inputs, RC); break; } case Hexagon::A2_xor: case Hexagon::A2_xorp: Eval = evaluateXORrr(R1, Register(Src2), Inputs, RC); break; } if (Eval) { Register DefR(MI.getOperand(0)); Outputs.update(DefR.Reg, RC); } return Eval; } bool HexagonConstEvaluator::evaluateHexCondMove(const MachineInstr &MI, const CellMap &Inputs, CellMap &Outputs) { // Dst0 = Cond1 ? Src2 : Src3 Register CR(MI.getOperand(1)); assert(Inputs.has(CR.Reg)); LatticeCell LS; if (!getCell(CR, Inputs, LS)) return false; uint32_t Ps = LS.properties(); unsigned TakeOp; if (Ps & ConstantProperties::Zero) TakeOp = 3; else if (Ps & ConstantProperties::NonZero) TakeOp = 2; else return false; const MachineOperand &ValOp = MI.getOperand(TakeOp); Register DefR(MI.getOperand(0)); LatticeCell RC = Outputs.get(DefR.Reg); if (ValOp.isImm()) { int64_t V = ValOp.getImm(); unsigned W = getRegBitWidth(DefR.Reg); APInt A(W, V, true); const Constant *C = intToConst(A); RC.add(C); Outputs.update(DefR.Reg, RC); return true; } if (ValOp.isReg()) { Register R(ValOp); const LatticeCell &LR = Inputs.get(R.Reg); LatticeCell LSR; if (!evaluate(R, LR, LSR)) return false; RC.meet(LSR); Outputs.update(DefR.Reg, RC); return true; } return false; } bool HexagonConstEvaluator::evaluateHexExt(const MachineInstr &MI, const CellMap &Inputs, CellMap &Outputs) { // Dst0 = ext R1 Register R1(MI.getOperand(1)); assert(Inputs.has(R1.Reg)); unsigned Opc = MI.getOpcode(); unsigned Bits; switch (Opc) { case Hexagon::A2_sxtb: case Hexagon::A2_zxtb: Bits = 8; break; case Hexagon::A2_sxth: case Hexagon::A2_zxth: Bits = 16; break; case Hexagon::A2_sxtw: Bits = 32; break; } bool Signed = false; switch (Opc) { case Hexagon::A2_sxtb: case Hexagon::A2_sxth: case Hexagon::A2_sxtw: Signed = true; break; } Register DefR(MI.getOperand(0)); unsigned BW = getRegBitWidth(DefR.Reg); LatticeCell RC = Outputs.get(DefR.Reg); bool Eval = Signed ? evaluateSEXTr(R1, BW, Bits, Inputs, RC) : evaluateZEXTr(R1, BW, Bits, Inputs, RC); if (!Eval) return false; Outputs.update(DefR.Reg, RC); return true; } bool HexagonConstEvaluator::evaluateHexVector1(const MachineInstr &MI, const CellMap &Inputs, CellMap &Outputs) { // DefR = op R1 Register DefR(MI.getOperand(0)); Register R1(MI.getOperand(1)); assert(Inputs.has(R1.Reg)); LatticeCell RC = Outputs.get(DefR.Reg); bool Eval; unsigned Opc = MI.getOpcode(); switch (Opc) { case Hexagon::S2_vsplatrb: // Rd = 4 times Rs:0..7 Eval = evaluateSplatr(R1, 8, 4, Inputs, RC); break; case Hexagon::S2_vsplatrh: // Rdd = 4 times Rs:0..15 Eval = evaluateSplatr(R1, 16, 4, Inputs, RC); break; default: return false; } if (!Eval) return false; Outputs.update(DefR.Reg, RC); return true; } bool HexagonConstEvaluator::rewriteHexConstDefs(MachineInstr &MI, const CellMap &Inputs, bool &AllDefs) { AllDefs = false; // Some diagnostics. // LLVM_DEBUG({...}) gets confused with all this code as an argument. #ifndef NDEBUG bool Debugging = DebugFlag && isCurrentDebugType(DEBUG_TYPE); if (Debugging) { bool Const = true, HasUse = false; for (const MachineOperand &MO : MI.operands()) { if (!MO.isReg() || !MO.isUse() || MO.isImplicit()) continue; Register R(MO); if (!TargetRegisterInfo::isVirtualRegister(R.Reg)) continue; HasUse = true; // PHIs can legitimately have "top" cells after propagation. if (!MI.isPHI() && !Inputs.has(R.Reg)) { dbgs() << "Top " << printReg(R.Reg, &HRI, R.SubReg) << " in MI: " << MI; continue; } const LatticeCell &L = Inputs.get(R.Reg); Const &= L.isSingle(); if (!Const) break; } if (HasUse && Const) { if (!MI.isCopy()) { dbgs() << "CONST: " << MI; for (const MachineOperand &MO : MI.operands()) { if (!MO.isReg() || !MO.isUse() || MO.isImplicit()) continue; unsigned R = MO.getReg(); dbgs() << printReg(R, &TRI) << ": " << Inputs.get(R) << "\n"; } } } } #endif // Avoid generating TFRIs for register transfers---this will keep the // coalescing opportunities. if (MI.isCopy()) return false; // Collect all virtual register-def operands. SmallVector DefRegs; for (const MachineOperand &MO : MI.operands()) { if (!MO.isReg() || !MO.isDef()) continue; unsigned R = MO.getReg(); if (!TargetRegisterInfo::isVirtualRegister(R)) continue; assert(!MO.getSubReg()); assert(Inputs.has(R)); DefRegs.push_back(R); } MachineBasicBlock &B = *MI.getParent(); const DebugLoc &DL = MI.getDebugLoc(); unsigned ChangedNum = 0; #ifndef NDEBUG SmallVector NewInstrs; #endif // For each defined register, if it is a constant, create an instruction // NewR = const // and replace all uses of the defined register with NewR. for (unsigned i = 0, n = DefRegs.size(); i < n; ++i) { unsigned R = DefRegs[i]; const LatticeCell &L = Inputs.get(R); if (L.isBottom()) continue; const TargetRegisterClass *RC = MRI->getRegClass(R); MachineBasicBlock::iterator At = MI.getIterator(); if (!L.isSingle()) { // If this a zero/non-zero cell, we can fold a definition // of a predicate register. using P = ConstantProperties; uint64_t Ps = L.properties(); if (!(Ps & (P::Zero|P::NonZero))) continue; const TargetRegisterClass *PredRC = &Hexagon::PredRegsRegClass; if (RC != PredRC) continue; const MCInstrDesc *NewD = (Ps & P::Zero) ? &HII.get(Hexagon::PS_false) : &HII.get(Hexagon::PS_true); unsigned NewR = MRI->createVirtualRegister(PredRC); const MachineInstrBuilder &MIB = BuildMI(B, At, DL, *NewD, NewR); (void)MIB; #ifndef NDEBUG NewInstrs.push_back(&*MIB); #endif replaceAllRegUsesWith(R, NewR); } else { // This cell has a single value. APInt A; if (!constToInt(L.Value, A) || !A.isSignedIntN(64)) continue; const TargetRegisterClass *NewRC; const MCInstrDesc *NewD; unsigned W = getRegBitWidth(R); int64_t V = A.getSExtValue(); assert(W == 32 || W == 64); if (W == 32) NewRC = &Hexagon::IntRegsRegClass; else NewRC = &Hexagon::DoubleRegsRegClass; unsigned NewR = MRI->createVirtualRegister(NewRC); const MachineInstr *NewMI; if (W == 32) { NewD = &HII.get(Hexagon::A2_tfrsi); NewMI = BuildMI(B, At, DL, *NewD, NewR) .addImm(V); } else { if (A.isSignedIntN(8)) { NewD = &HII.get(Hexagon::A2_tfrpi); NewMI = BuildMI(B, At, DL, *NewD, NewR) .addImm(V); } else { int32_t Hi = V >> 32; int32_t Lo = V & 0xFFFFFFFFLL; if (isInt<8>(Hi) && isInt<8>(Lo)) { NewD = &HII.get(Hexagon::A2_combineii); NewMI = BuildMI(B, At, DL, *NewD, NewR) .addImm(Hi) .addImm(Lo); } else { NewD = &HII.get(Hexagon::CONST64); NewMI = BuildMI(B, At, DL, *NewD, NewR) .addImm(V); } } } (void)NewMI; #ifndef NDEBUG NewInstrs.push_back(NewMI); #endif replaceAllRegUsesWith(R, NewR); } ChangedNum++; } LLVM_DEBUG({ if (!NewInstrs.empty()) { MachineFunction &MF = *MI.getParent()->getParent(); dbgs() << "In function: " << MF.getName() << "\n"; dbgs() << "Rewrite: for " << MI << " created " << *NewInstrs[0]; for (unsigned i = 1; i < NewInstrs.size(); ++i) dbgs() << " " << *NewInstrs[i]; } }); AllDefs = (ChangedNum == DefRegs.size()); return ChangedNum > 0; } bool HexagonConstEvaluator::rewriteHexConstUses(MachineInstr &MI, const CellMap &Inputs) { bool Changed = false; unsigned Opc = MI.getOpcode(); MachineBasicBlock &B = *MI.getParent(); const DebugLoc &DL = MI.getDebugLoc(); MachineBasicBlock::iterator At = MI.getIterator(); MachineInstr *NewMI = nullptr; switch (Opc) { case Hexagon::M2_maci: // Convert DefR += mpyi(R2, R3) // to DefR += mpyi(R, #imm), // or DefR -= mpyi(R, #imm). { Register DefR(MI.getOperand(0)); assert(!DefR.SubReg); Register R2(MI.getOperand(2)); Register R3(MI.getOperand(3)); assert(Inputs.has(R2.Reg) && Inputs.has(R3.Reg)); LatticeCell LS2, LS3; // It is enough to get one of the input cells, since we will only try // to replace one argument---whichever happens to be a single constant. bool HasC2 = getCell(R2, Inputs, LS2), HasC3 = getCell(R3, Inputs, LS3); if (!HasC2 && !HasC3) return false; bool Zero = ((HasC2 && (LS2.properties() & ConstantProperties::Zero)) || (HasC3 && (LS3.properties() & ConstantProperties::Zero))); // If one of the operands is zero, eliminate the multiplication. if (Zero) { // DefR == R1 (tied operands). MachineOperand &Acc = MI.getOperand(1); Register R1(Acc); unsigned NewR = R1.Reg; if (R1.SubReg) { // Generate COPY. FIXME: Replace with the register:subregister. const TargetRegisterClass *RC = MRI->getRegClass(DefR.Reg); NewR = MRI->createVirtualRegister(RC); NewMI = BuildMI(B, At, DL, HII.get(TargetOpcode::COPY), NewR) .addReg(R1.Reg, getRegState(Acc), R1.SubReg); } replaceAllRegUsesWith(DefR.Reg, NewR); MRI->clearKillFlags(NewR); Changed = true; break; } bool Swap = false; if (!LS3.isSingle()) { if (!LS2.isSingle()) return false; Swap = true; } const LatticeCell &LI = Swap ? LS2 : LS3; const MachineOperand &OpR2 = Swap ? MI.getOperand(3) : MI.getOperand(2); // LI is single here. APInt A; if (!constToInt(LI.Value, A) || !A.isSignedIntN(8)) return false; int64_t V = A.getSExtValue(); const MCInstrDesc &D = (V >= 0) ? HII.get(Hexagon::M2_macsip) : HII.get(Hexagon::M2_macsin); if (V < 0) V = -V; const TargetRegisterClass *RC = MRI->getRegClass(DefR.Reg); unsigned NewR = MRI->createVirtualRegister(RC); const MachineOperand &Src1 = MI.getOperand(1); NewMI = BuildMI(B, At, DL, D, NewR) .addReg(Src1.getReg(), getRegState(Src1), Src1.getSubReg()) .addReg(OpR2.getReg(), getRegState(OpR2), OpR2.getSubReg()) .addImm(V); replaceAllRegUsesWith(DefR.Reg, NewR); Changed = true; break; } case Hexagon::A2_and: { Register R1(MI.getOperand(1)); Register R2(MI.getOperand(2)); assert(Inputs.has(R1.Reg) && Inputs.has(R2.Reg)); LatticeCell LS1, LS2; unsigned CopyOf = 0; // Check if any of the operands is -1 (i.e. all bits set). if (getCell(R1, Inputs, LS1) && LS1.isSingle()) { APInt M1; if (constToInt(LS1.Value, M1) && !~M1) CopyOf = 2; } else if (getCell(R2, Inputs, LS2) && LS2.isSingle()) { APInt M1; if (constToInt(LS2.Value, M1) && !~M1) CopyOf = 1; } if (!CopyOf) return false; MachineOperand &SO = MI.getOperand(CopyOf); Register SR(SO); Register DefR(MI.getOperand(0)); unsigned NewR = SR.Reg; if (SR.SubReg) { const TargetRegisterClass *RC = MRI->getRegClass(DefR.Reg); NewR = MRI->createVirtualRegister(RC); NewMI = BuildMI(B, At, DL, HII.get(TargetOpcode::COPY), NewR) .addReg(SR.Reg, getRegState(SO), SR.SubReg); } replaceAllRegUsesWith(DefR.Reg, NewR); MRI->clearKillFlags(NewR); Changed = true; } break; case Hexagon::A2_or: { Register R1(MI.getOperand(1)); Register R2(MI.getOperand(2)); assert(Inputs.has(R1.Reg) && Inputs.has(R2.Reg)); LatticeCell LS1, LS2; unsigned CopyOf = 0; using P = ConstantProperties; if (getCell(R1, Inputs, LS1) && (LS1.properties() & P::Zero)) CopyOf = 2; else if (getCell(R2, Inputs, LS2) && (LS2.properties() & P::Zero)) CopyOf = 1; if (!CopyOf) return false; MachineOperand &SO = MI.getOperand(CopyOf); Register SR(SO); Register DefR(MI.getOperand(0)); unsigned NewR = SR.Reg; if (SR.SubReg) { const TargetRegisterClass *RC = MRI->getRegClass(DefR.Reg); NewR = MRI->createVirtualRegister(RC); NewMI = BuildMI(B, At, DL, HII.get(TargetOpcode::COPY), NewR) .addReg(SR.Reg, getRegState(SO), SR.SubReg); } replaceAllRegUsesWith(DefR.Reg, NewR); MRI->clearKillFlags(NewR); Changed = true; } break; } if (NewMI) { // clear all the kill flags of this new instruction. for (MachineOperand &MO : NewMI->operands()) if (MO.isReg() && MO.isUse()) MO.setIsKill(false); } LLVM_DEBUG({ if (NewMI) { dbgs() << "Rewrite: for " << MI; if (NewMI != &MI) dbgs() << " created " << *NewMI; else dbgs() << " modified the instruction itself and created:" << *NewMI; } }); return Changed; } void HexagonConstEvaluator::replaceAllRegUsesWith(unsigned FromReg, unsigned ToReg) { assert(TargetRegisterInfo::isVirtualRegister(FromReg)); assert(TargetRegisterInfo::isVirtualRegister(ToReg)); for (auto I = MRI->use_begin(FromReg), E = MRI->use_end(); I != E;) { MachineOperand &O = *I; ++I; O.setReg(ToReg); } } bool HexagonConstEvaluator::rewriteHexBranch(MachineInstr &BrI, const CellMap &Inputs) { MachineBasicBlock &B = *BrI.getParent(); unsigned NumOp = BrI.getNumOperands(); if (!NumOp) return false; bool FallsThru; SetVector Targets; bool Eval = evaluate(BrI, Inputs, Targets, FallsThru); unsigned NumTargets = Targets.size(); if (!Eval || NumTargets > 1 || (NumTargets == 1 && FallsThru)) return false; if (BrI.getOpcode() == Hexagon::J2_jump) return false; LLVM_DEBUG(dbgs() << "Rewrite(" << printMBBReference(B) << "):" << BrI); bool Rewritten = false; if (NumTargets > 0) { assert(!FallsThru && "This should have been checked before"); // MIB.addMBB needs non-const pointer. MachineBasicBlock *TargetB = const_cast(Targets[0]); bool Moot = B.isLayoutSuccessor(TargetB); if (!Moot) { // If we build a branch here, we must make sure that it won't be // erased as "non-executable". We can't mark any new instructions // as executable here, so we need to overwrite the BrI, which we // know is executable. const MCInstrDesc &JD = HII.get(Hexagon::J2_jump); auto NI = BuildMI(B, BrI.getIterator(), BrI.getDebugLoc(), JD) .addMBB(TargetB); BrI.setDesc(JD); while (BrI.getNumOperands() > 0) BrI.RemoveOperand(0); // This ensures that all implicit operands (e.g. implicit-def %r31, etc) // are present in the rewritten branch. for (auto &Op : NI->operands()) BrI.addOperand(Op); NI->eraseFromParent(); Rewritten = true; } } // Do not erase instructions. A newly created instruction could get // the same address as an instruction marked as executable during the // propagation. if (!Rewritten) replaceWithNop(BrI); return true; } FunctionPass *llvm::createHexagonConstPropagationPass() { return new HexagonConstPropagation(); }