From 167d918225ecd05a63f6b8d449c317feda741e18 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Thu, 28 Jul 2016 20:01:59 +0000 Subject: [PATCH] [Hexagon] Implement MI-level constant propagation llvm-svn: 277028 --- llvm/lib/Target/Hexagon/CMakeLists.txt | 1 + .../Hexagon/HexagonConstPropagation.cpp | 3208 +++++++++++++++++ .../Target/Hexagon/HexagonTargetMachine.cpp | 9 + llvm/test/CodeGen/Hexagon/constp-clb.ll | 23 + .../CodeGen/Hexagon/constp-combine-neg.ll | 27 + llvm/test/CodeGen/Hexagon/constp-ctb.ll | 26 + llvm/test/CodeGen/Hexagon/constp-extract.ll | 31 + llvm/test/CodeGen/Hexagon/constp-physreg.ll | 21 + .../Hexagon/constp-rewrite-branches.ll | 17 + llvm/test/CodeGen/Hexagon/constp-rseq.ll | 19 + llvm/test/CodeGen/Hexagon/constp-vsplat.ll | 18 + .../test/CodeGen/Hexagon/vect/vect-vsplatb.ll | 2 +- .../test/CodeGen/Hexagon/vect/vect-vsplath.ll | 2 +- 13 files changed, 3402 insertions(+), 2 deletions(-) create mode 100644 llvm/lib/Target/Hexagon/HexagonConstPropagation.cpp create mode 100644 llvm/test/CodeGen/Hexagon/constp-clb.ll create mode 100644 llvm/test/CodeGen/Hexagon/constp-combine-neg.ll create mode 100644 llvm/test/CodeGen/Hexagon/constp-ctb.ll create mode 100644 llvm/test/CodeGen/Hexagon/constp-extract.ll create mode 100644 llvm/test/CodeGen/Hexagon/constp-physreg.ll create mode 100644 llvm/test/CodeGen/Hexagon/constp-rewrite-branches.ll create mode 100644 llvm/test/CodeGen/Hexagon/constp-rseq.ll create mode 100644 llvm/test/CodeGen/Hexagon/constp-vsplat.ll diff --git a/llvm/lib/Target/Hexagon/CMakeLists.txt b/llvm/lib/Target/Hexagon/CMakeLists.txt index 0e32f25f52b7..06214149c698 100644 --- a/llvm/lib/Target/Hexagon/CMakeLists.txt +++ b/llvm/lib/Target/Hexagon/CMakeLists.txt @@ -21,6 +21,7 @@ add_llvm_target(HexagonCodeGen HexagonBranchRelaxation.cpp HexagonCFGOptimizer.cpp HexagonCommonGEP.cpp + HexagonConstPropagation.cpp HexagonCopyToCombine.cpp HexagonEarlyIfConv.cpp HexagonExpandCondsets.cpp diff --git a/llvm/lib/Target/Hexagon/HexagonConstPropagation.cpp b/llvm/lib/Target/Hexagon/HexagonConstPropagation.cpp new file mode 100644 index 000000000000..930e2f7766f3 --- /dev/null +++ b/llvm/lib/Target/Hexagon/HexagonConstPropagation.cpp @@ -0,0 +1,3208 @@ +//===--- 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/PostOrderIterator.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/Constants.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetInstrInfo.h" + +#include +#include +#include + +using namespace llvm; + +namespace { + class LatticeCell; + + // 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 = 0) 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] = 0; + } + + 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(); + }; + + raw_ostream &operator<< (raw_ostream &os, const LatticeCell &L) { + L.print(os); + return os; + } + + 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: + typedef std::map MapType; + 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: + typedef MapType::const_iterator 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 isExecutable(const MachineBasicBlock *MB) const; + void pushLayoutSuccessor(const MachineBasicBlock *MB); + 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; + + typedef std::pair CFGEdge; + typedef std::set SetOfCFGEdge; + typedef std::set SetOfInstr; + typedef std::queue QueueOfCFGEdge; + + 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() {} + + // 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. + typedef MachineConstPropagator::CellMap 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); + }; + +} + +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; +} + + +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 << " }"; +} + + +// "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; +} + + +void MachineConstPropagator::CellMap::print(raw_ostream &os, + const TargetRegisterInfo &TRI) const { + for (auto &I : Map) + dbgs() << " " << PrintReg(I.first, &TRI) << " -> " << I.second << '\n'; +} + + +void MachineConstPropagator::visitPHI(const MachineInstr &PN) { + const MachineBasicBlock *MB = PN.getParent(); + unsigned MBN = MB->getNumber(); + DEBUG(dbgs() << "Visiting FI(BB#" << MBN << "): " << 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))) { + DEBUG(dbgs() << " edge BB#" << PBN << "->BB#" << MBN + << " 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); + DEBUG(dbgs() << " edge from BB#" << PBN << ": " + << 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) { + DEBUG(dbgs() << "Visiting MI(BB#" << MI.getParent()->getNumber() + << "): " << MI); + CellMap Outputs; + bool Eval = MCE.evaluate(MI, Cells, Outputs); + 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); + } +} + + +// \brief 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); + DEBUG(dbgs() << "Visiting " << (EvalOk ? "BR" : "br") << "(BB#" + << MBN << "): " << 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(); + 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(); + DEBUG(dbgs() << " pushing edge BB#" << MBN << " -> BB#" << TBN << "\n"); + FlowQ.push(CFGEdge(MBN, TBN)); + } +} + + +void MachineConstPropagator::visitUsesOf(unsigned Reg) { + 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::isExecutable(const MachineBasicBlock *MB) const { + unsigned MBN = MB->getNumber(); + for (const MachineBasicBlock *PB : MB->predecessors()) { + unsigned PBN = PB->getNumber(); + if (EdgeExec.count(CFGEdge(PBN, MBN))) + return true; + } + return false; +} + + +void MachineConstPropagator::pushLayoutSuccessor(const MachineBasicBlock *MB) { + MachineFunction::const_iterator BI = MB->getIterator(); + unsigned MBN = MB->getNumber(); + unsigned SBN = std::next(BI)->getNumber(); + FlowQ.push(CFGEdge(MBN, SBN)); +} + + +bool MachineConstPropagator::computeBlockSuccessors(const MachineBasicBlock *MB, + SetVector &Targets) { + MachineBasicBlock::const_iterator FirstBr = MB->end(); + for (const MachineInstr &MI : *MB) { + if (MI.isDebugValue()) + 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.isDebugValue()) + 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(); + + DEBUG(dbgs() << "Picked edge BB#" << Edge.first << "->BB#" + << 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->isDebugValue()) + ++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->isDebugValue()) { + 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) + + 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() << " BB#" << BN << " -> BB#" << SN << '\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) { + DEBUG(MF.print(dbgs() << "Starting MachineConstPropagator\n", 0)); + + MRI = &MF.getRegInfo(); + + Cells.clear(); + EdgeExec.clear(); + InstrExec.clear(); + assert(FlowQ.empty()); + + propagate(MF); + bool Changed = rewrite(MF); + + DEBUG({ + dbgs() << "End of MachineConstPropagator (Changed=" << Changed << ")\n"; + if (Changed) + MF.print(dbgs(), 0); + }); + 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) { + typedef ConstantProperties P; + 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(); + 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); +} + +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) { + PassRegistry &Registry = *PassRegistry::getPassRegistry(); + initializeHexagonConstPropagationPass(Registry); + } + const char *getPassName() const override { + return "Hexagon Constant Propagation"; + } + bool runOnMachineFunction(MachineFunction &MF) override { + const Function *F = MF.getFunction(); + if (!F) + return false; + if (skipFunction(*F)) + return false; + + HexagonConstEvaluator HCE(MF); + return MachineConstPropagator(HCE).run(MF); + } + }; + + char HexagonConstPropagation::ID = 0; +} + +INITIALIZE_PASS(HexagonConstPropagation, "hcp", "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(); + if (Sub1 != Hexagon::subreg_loreg && Sub1 != Hexagon::subreg_hireg) + return false; + if (Sub2 != Hexagon::subreg_loreg && Sub2 != Hexagon::subreg_hireg) + return false; + assert(Sub1 != Sub2); + bool LoIs1 = (Sub1 == Hexagon::subreg_loreg); + 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::CONST32: + case Hexagon::A2_tfrpi: + case Hexagon::CONST32_Int_Real: + case Hexagon::CONST64_Int_Real: + { + const MachineOperand &VO = MI.getOperand(1); + // The operand of CONST32_Int_Real can be a blockaddress, e.g. + // %vreg0 = CONST32_Int_Real + // 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::TFR_PdTrue: + case Hexagon::TFR_PdFalse: + { + LatticeCell RC = Outputs.get(DefR.Reg); + bool NonZero = (Opc == Hexagon::TFR_PdTrue); + 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) + { + int64_t Hi = MI.getOperand(1).getImm(); + int64_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, (1 << 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; + } + // Predicate registers do not have subregisters. + const TargetRegisterClass *RC = MRI->getRegClass(R.Reg); + if (RC == &Hexagon::PredRegsRegClass) + return false; + if (R.SubReg != Hexagon::subreg_loreg && R.SubReg != Hexagon::subreg_hireg) + return false; + + assert(!Input.isTop()); + if (Input.isBottom()) + return false; + + typedef ConstantProperties P; + 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::subreg_hireg) { + 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::subreg_hireg) + 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; + 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::CONST32: + case Hexagon::A2_tfrpi: + case Hexagon::CONST32_Int_Real: + case Hexagon::CONST64_Int_Real: + case Hexagon::TFR_PdTrue: + case Hexagon::TFR_PdFalse: + 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; + 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: { + 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: { + 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. + // DEBUG({...}) gets confused with all this code as an argument. +#ifndef NDEBUG + bool Debugging = llvm::DebugFlag && + llvm::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. + typedef ConstantProperties P; + 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::TFR_PdFalse) : + &HII.get(Hexagon::TFR_PdTrue); + 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_Int_Real); + NewMI = BuildMI(B, At, DL, *NewD, NewR) + .addImm(V); + } + } + } + (void)NewMI; +#ifndef NDEBUG + NewInstrs.push_back(NewMI); +#endif + replaceAllRegUsesWith(R, NewR); + } + ChangedNum++; + } + + DEBUG({ + if (!NewInstrs.empty()) { + MachineFunction &MF = *MI.getParent()->getParent(); + dbgs() << "In function: " << MF.getFunction()->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 = NULL; + + 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; + typedef ConstantProperties P; + 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); + } + + 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; + + DEBUG(dbgs() << "Rewrite(BB#" << B.getNumber() << "):" << 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. %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(); +} + diff --git a/llvm/lib/Target/Hexagon/HexagonTargetMachine.cpp b/llvm/lib/Target/Hexagon/HexagonTargetMachine.cpp index 2c971b12ba84..80f256cd5865 100644 --- a/llvm/lib/Target/Hexagon/HexagonTargetMachine.cpp +++ b/llvm/lib/Target/Hexagon/HexagonTargetMachine.cpp @@ -42,6 +42,9 @@ static cl::opt DisableHexagonCFGOpt("disable-hexagon-cfgopt", cl::Hidden, cl::ZeroOrMore, cl::init(false), cl::desc("Disable Hexagon CFG Optimization")); +static cl::opt DisableHCP("disable-hcp", cl::init(false), cl::Hidden, + cl::ZeroOrMore, cl::desc("Disable Hexagon constant propagation")); + static cl::opt DisableStoreWidening("disable-store-widen", cl::Hidden, cl::init(false), cl::desc("Disable store widening")); @@ -111,6 +114,7 @@ namespace llvm { FunctionPass *createHexagonCallFrameInformation(); FunctionPass *createHexagonCFGOptimizer(); FunctionPass *createHexagonCommonGEP(); + FunctionPass *createHexagonConstPropagationPass(); FunctionPass *createHexagonCopyToCombine(); FunctionPass *createHexagonEarlyIfConversion(); FunctionPass *createHexagonExpandCondsets(); @@ -263,6 +267,11 @@ bool HexagonPassConfig::addInstSelector() { addPass(createHexagonBitSimplify(), false); addPass(createHexagonPeephole()); printAndVerify("After hexagon peephole pass"); + // Constant propagation. + if (!DisableHCP) { + addPass(createHexagonConstPropagationPass(), false); + addPass(&UnreachableMachineBlockElimID, false); + } if (EnableGenInsert) addPass(createHexagonGenInsert(), false); if (EnableEarlyIf) diff --git a/llvm/test/CodeGen/Hexagon/constp-clb.ll b/llvm/test/CodeGen/Hexagon/constp-clb.ll new file mode 100644 index 000000000000..1a872b11aad3 --- /dev/null +++ b/llvm/test/CodeGen/Hexagon/constp-clb.ll @@ -0,0 +1,23 @@ +; RUN: llc -mcpu=hexagonv5 < %s +; REQUIRES: asserts + +target datalayout = "e-m:e-p:32:32-i1:32-i64:64-a:0-v32:32-n16:32" +target triple = "hexagon-unknown--elf" + +; Function Attrs: nounwind readnone +define i64 @foo() #0 { +entry: + %0 = tail call i32 @llvm.hexagon.S2.clbp(i64 291) + %1 = tail call i64 @llvm.hexagon.A4.combineir(i32 0, i32 %0) + ret i64 %1 +} + +; Function Attrs: nounwind readnone +declare i32 @llvm.hexagon.S2.clbp(i64) #1 + +; Function Attrs: nounwind readnone +declare i64 @llvm.hexagon.A4.combineir(i32, i32) #1 + +attributes #0 = { nounwind readnone "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } + diff --git a/llvm/test/CodeGen/Hexagon/constp-combine-neg.ll b/llvm/test/CodeGen/Hexagon/constp-combine-neg.ll new file mode 100644 index 000000000000..18f0e81076af --- /dev/null +++ b/llvm/test/CodeGen/Hexagon/constp-combine-neg.ll @@ -0,0 +1,27 @@ +; RUN: llc -O2 -march=hexagon < %s | FileCheck %s --check-prefix=CHECK-TEST1 +; RUN: llc -O2 -march=hexagon < %s | FileCheck %s --check-prefix=CHECK-TEST2 +; RUN: llc -O2 -march=hexagon < %s | FileCheck %s --check-prefix=CHECK-TEST3 +define i32 @main() #0 { +entry: + %l = alloca [7 x i32], align 8 + %p_arrayidx45 = bitcast [7 x i32]* %l to i32* + %vector_ptr = bitcast [7 x i32]* %l to <2 x i32>* + store <2 x i32> , <2 x i32>* %vector_ptr, align 8 + %p_arrayidx.1 = getelementptr [7 x i32], [7 x i32]* %l, i32 0, i32 2 + %vector_ptr.1 = bitcast i32* %p_arrayidx.1 to <2 x i32>* + store <2 x i32> , <2 x i32>* %vector_ptr.1, align 8 + %p_arrayidx.2 = getelementptr [7 x i32], [7 x i32]* %l, i32 0, i32 4 + %vector_ptr.2 = bitcast i32* %p_arrayidx.2 to <2 x i32>* + store <2 x i32> , <2 x i32>* %vector_ptr.2, align 8 + ret i32 0 +} + +; The instructions seem to be in a different order in the .s file than +; the corresponding values in the .ll file, so just run the test three +; times and each time test for a different instruction. +; CHECK-TEST1: combine(#-2, #3) +; CHECK-TEST2: combine(#6, #-4) +; CHECK-TEST3: combine(#-10, #-8) + +attributes #0 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-frame-pointer-elim-non-leaf"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="false" "use-soft-float"="false" } + diff --git a/llvm/test/CodeGen/Hexagon/constp-ctb.ll b/llvm/test/CodeGen/Hexagon/constp-ctb.ll new file mode 100644 index 000000000000..76a9820583ea --- /dev/null +++ b/llvm/test/CodeGen/Hexagon/constp-ctb.ll @@ -0,0 +1,26 @@ +; RUN: llc < %s +; REQUIRES: asserts + +target datalayout = "e-m:e-p:32:32-i1:32-i64:64-a:0-v32:32-n16:32" +target triple = "hexagon-unknown--elf" + +; Function Attrs: nounwind readnone +define i64 @foo() #0 { +entry: + %0 = tail call i32 @llvm.hexagon.S2.ct0p(i64 18) + %1 = tail call i32 @llvm.hexagon.S2.ct1p(i64 27) + %2 = tail call i64 @llvm.hexagon.A2.combinew(i32 %0, i32 %1) + ret i64 %2 +} + +; Function Attrs: nounwind readnone +declare i32 @llvm.hexagon.S2.ct0p(i64) #0 + +; Function Attrs: nounwind readnone +declare i32 @llvm.hexagon.S2.ct1p(i64) #0 + +; Function Attrs: nounwind readnone +declare i64 @llvm.hexagon.A2.combinew(i32, i32) #0 + +attributes #0 = { nounwind readnone } + diff --git a/llvm/test/CodeGen/Hexagon/constp-extract.ll b/llvm/test/CodeGen/Hexagon/constp-extract.ll new file mode 100644 index 000000000000..00f176317c4c --- /dev/null +++ b/llvm/test/CodeGen/Hexagon/constp-extract.ll @@ -0,0 +1,31 @@ +; Expect the constant propagation to evaluate signed and unsigned bit extract. +; RUN: llc -march=hexagon -O2 < %s | FileCheck %s + +target triple = "hexagon" + +@x = common global i32 0, align 4 +@y = common global i32 0, align 4 + +define void @foo() #0 { +entry: + ; extractu(0x000ABCD0, 16, 4) + ; should evaluate to 0xABCD (dec 43981) + %0 = call i32 @llvm.hexagon.S2.extractu(i32 703696, i32 16, i32 4) +; CHECK: 43981 +; CHECK-NOT: extractu + store i32 %0, i32* @x, align 4 + ; extract(0x000ABCD0, 16, 4) + ; should evaluate to 0xFFFFABCD (dec 4294945741 or -21555) + %1 = call i32 @llvm.hexagon.S4.extract(i32 703696, i32 16, i32 4) +; CHECK: -21555 +; CHECK-NOT: extract + store i32 %1, i32* @y, align 4 + ret void +} + +declare i32 @llvm.hexagon.S2.extractu(i32, i32, i32) #1 + +declare i32 @llvm.hexagon.S4.extract(i32, i32, i32) #1 + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } diff --git a/llvm/test/CodeGen/Hexagon/constp-physreg.ll b/llvm/test/CodeGen/Hexagon/constp-physreg.ll new file mode 100644 index 000000000000..0473b96f6de4 --- /dev/null +++ b/llvm/test/CodeGen/Hexagon/constp-physreg.ll @@ -0,0 +1,21 @@ +; RUN: llc -O2 -march hexagon < %s +target datalayout = "e-p:32:32:32-i64:64:64-i32:32:32-i16:16:16-i1:32:32-f64:64:64-f32:32:32-v64:64:64-v32:32:32-a0:0-n16:32" +target triple = "hexagon" + +define signext i16 @foo(i16 signext %var1, i16 signext %var2) #0 { +entry: + %0 = or i16 %var2, %var1 + %1 = icmp slt i16 %0, 0 + %cmp8 = icmp sgt i16 %var1, %var2 + %or.cond19 = or i1 %1, %cmp8 + br i1 %or.cond19, label %return, label %if.end + +if.end: ; preds = %entry + br label %return + +return: ; preds = %if.end, %if.end15, %entry + %retval.0.reg2mem.0 = phi i16 [ 0, %entry ], [ 32767, %if.end ] + ret i16 %retval.0.reg2mem.0 +} + +attributes #0 = { nounwind readnone "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-frame-pointer-elim-non-leaf"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="false" "use-soft-float"="false" } diff --git a/llvm/test/CodeGen/Hexagon/constp-rewrite-branches.ll b/llvm/test/CodeGen/Hexagon/constp-rewrite-branches.ll new file mode 100644 index 000000000000..dfac49cb553e --- /dev/null +++ b/llvm/test/CodeGen/Hexagon/constp-rewrite-branches.ll @@ -0,0 +1,17 @@ +; RUN: llc -O2 -march hexagon < %s | FileCheck %s + +define i32 @foo(i32 %x) { + %p = icmp eq i32 %x, 0 + br i1 %p, label %zero, label %nonzero +nonzero: + %v1 = add i32 %x, 1 + %c = icmp eq i32 %x, %v1 +; This branch will be rewritten by HCP. A bug would cause both branches to +; go away, leaving no path to "ret -1". + br i1 %c, label %zero, label %other +zero: + ret i32 0 +other: +; CHECK: -1 + ret i32 -1 +} diff --git a/llvm/test/CodeGen/Hexagon/constp-rseq.ll b/llvm/test/CodeGen/Hexagon/constp-rseq.ll new file mode 100644 index 000000000000..c89407e4b8e4 --- /dev/null +++ b/llvm/test/CodeGen/Hexagon/constp-rseq.ll @@ -0,0 +1,19 @@ +; RUN: llc -march=hexagon < %s | FileCheck %s +; CHECK: cmp +; Make sure that the result is not a compile-time constant. + +define i64 @foo(i32 %x) { +entry: + %c = icmp slt i32 %x, 17 + br i1 %c, label %b1, label %b2 +b1: + br label %b2 +b2: + %p = phi i32 [ 1, %entry ], [ 0, %b1 ] + %q = sub i32 %x, %x + %y = zext i32 %q to i64 + %u = shl i64 %y, 32 + %v = zext i32 %p to i64 + %w = or i64 %u, %v + ret i64 %w +} diff --git a/llvm/test/CodeGen/Hexagon/constp-vsplat.ll b/llvm/test/CodeGen/Hexagon/constp-vsplat.ll new file mode 100644 index 000000000000..6063383a5f75 --- /dev/null +++ b/llvm/test/CodeGen/Hexagon/constp-vsplat.ll @@ -0,0 +1,18 @@ +; RUN: llc < %s +; REQUIRES: asserts +target datalayout = "e-m:e-p:32:32-i1:32-i64:64-a:0-v32:32-n16:32" +target triple = "hexagon" + +; Function Attrs: nounwind readnone +define i64 @foo() #0 { +entry: + %0 = tail call i32 @llvm.hexagon.S2.vsplatrb(i32 255) + %conv = zext i32 %0 to i64 + %shl = shl nuw i64 %conv, 32 + %or = or i64 %shl, %conv + ret i64 %or +} + +declare i32 @llvm.hexagon.S2.vsplatrb(i32) #0 + +attributes #0 = { nounwind readnone } diff --git a/llvm/test/CodeGen/Hexagon/vect/vect-vsplatb.ll b/llvm/test/CodeGen/Hexagon/vect/vect-vsplatb.ll index 6996dd144eba..097e2ccd600a 100644 --- a/llvm/test/CodeGen/Hexagon/vect/vect-vsplatb.ll +++ b/llvm/test/CodeGen/Hexagon/vect/vect-vsplatb.ll @@ -1,4 +1,4 @@ -; RUN: llc -march=hexagon < %s | FileCheck %s +; RUN: llc -march=hexagon -disable-hcp < %s | FileCheck %s ; Make sure we build the constant vector <7, 7, 7, 7> with a vsplatb. ; CHECK: vsplatb @B = common global [400 x i8] zeroinitializer, align 8 diff --git a/llvm/test/CodeGen/Hexagon/vect/vect-vsplath.ll b/llvm/test/CodeGen/Hexagon/vect/vect-vsplath.ll index f5207109773e..db90bf42be2a 100644 --- a/llvm/test/CodeGen/Hexagon/vect/vect-vsplath.ll +++ b/llvm/test/CodeGen/Hexagon/vect/vect-vsplath.ll @@ -1,4 +1,4 @@ -; RUN: llc -march=hexagon < %s | FileCheck %s +; RUN: llc -march=hexagon -disable-hcp < %s | FileCheck %s ; Make sure we build the constant vector <7, 7, 7, 7> with a vsplath. ; CHECK: vsplath @B = common global [400 x i16] zeroinitializer, align 8 -- GitLab