| //===- BitTracker.h ---------------------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_LIB_TARGET_HEXAGON_BITTRACKER_H |
| #define LLVM_LIB_TARGET_HEXAGON_BITTRACKER_H |
| |
| #include "llvm/ADT/DenseSet.h" |
| #include "llvm/ADT/SetVector.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/CodeGen/MachineInstr.h" |
| #include "llvm/CodeGen/MachineOperand.h" |
| #include <cassert> |
| #include <cstdint> |
| #include <map> |
| #include <queue> |
| #include <set> |
| #include <utility> |
| |
| namespace llvm { |
| |
| class BitVector; |
| class ConstantInt; |
| class MachineRegisterInfo; |
| class MachineBasicBlock; |
| class MachineFunction; |
| class raw_ostream; |
| class TargetRegisterClass; |
| class TargetRegisterInfo; |
| |
| struct BitTracker { |
| struct BitRef; |
| struct RegisterRef; |
| struct BitValue; |
| struct BitMask; |
| struct RegisterCell; |
| struct MachineEvaluator; |
| |
| using BranchTargetList = SetVector<const MachineBasicBlock *>; |
| using CellMapType = std::map<unsigned, RegisterCell>; |
| |
| BitTracker(const MachineEvaluator &E, MachineFunction &F); |
| ~BitTracker(); |
| |
| void run(); |
| void trace(bool On = false) { Trace = On; } |
| bool has(unsigned Reg) const; |
| const RegisterCell &lookup(unsigned Reg) const; |
| RegisterCell get(RegisterRef RR) const; |
| void put(RegisterRef RR, const RegisterCell &RC); |
| void subst(RegisterRef OldRR, RegisterRef NewRR); |
| bool reached(const MachineBasicBlock *B) const; |
| void visit(const MachineInstr &MI); |
| |
| void print_cells(raw_ostream &OS) const; |
| |
| private: |
| void visitPHI(const MachineInstr &PI); |
| void visitNonBranch(const MachineInstr &MI); |
| void visitBranchesFrom(const MachineInstr &BI); |
| void visitUsesOf(Register Reg); |
| |
| using CFGEdge = std::pair<int, int>; |
| using EdgeSetType = std::set<CFGEdge>; |
| using InstrSetType = std::set<const MachineInstr *>; |
| using EdgeQueueType = std::queue<CFGEdge>; |
| |
| // Priority queue of instructions using modified registers, ordered by |
| // their relative position in a basic block. |
| struct UseQueueType { |
| UseQueueType() : Uses(Dist) {} |
| |
| unsigned size() const { |
| return Uses.size(); |
| } |
| bool empty() const { |
| return size() == 0; |
| } |
| MachineInstr *front() const { |
| return Uses.top(); |
| } |
| void push(MachineInstr *MI) { |
| if (Set.insert(MI).second) |
| Uses.push(MI); |
| } |
| void pop() { |
| Set.erase(front()); |
| Uses.pop(); |
| } |
| void reset() { |
| Dist.clear(); |
| } |
| private: |
| struct Cmp { |
| Cmp(DenseMap<const MachineInstr*,unsigned> &Map) : Dist(Map) {} |
| bool operator()(const MachineInstr *MI, const MachineInstr *MJ) const; |
| DenseMap<const MachineInstr*,unsigned> &Dist; |
| }; |
| std::priority_queue<MachineInstr*, std::vector<MachineInstr*>, Cmp> Uses; |
| DenseSet<const MachineInstr*> Set; // Set to avoid adding duplicate entries. |
| DenseMap<const MachineInstr*,unsigned> Dist; |
| }; |
| |
| void reset(); |
| void runEdgeQueue(BitVector &BlockScanned); |
| void runUseQueue(); |
| |
| const MachineEvaluator &ME; |
| MachineFunction &MF; |
| MachineRegisterInfo &MRI; |
| CellMapType ⤅ |
| |
| EdgeSetType EdgeExec; // Executable flow graph edges. |
| InstrSetType InstrExec; // Executable instructions. |
| UseQueueType UseQ; // Work queue of register uses. |
| EdgeQueueType FlowQ; // Work queue of CFG edges. |
| DenseSet<unsigned> ReachedBB; // Cache of reached blocks. |
| bool Trace; // Enable tracing for debugging. |
| }; |
| |
| // Abstraction of a reference to bit at position Pos from a register Reg. |
| struct BitTracker::BitRef { |
| BitRef(unsigned R = 0, uint16_t P = 0) : Reg(R), Pos(P) {} |
| |
| bool operator== (const BitRef &BR) const { |
| // If Reg is 0, disregard Pos. |
| return Reg == BR.Reg && (Reg == 0 || Pos == BR.Pos); |
| } |
| |
| Register Reg; |
| uint16_t Pos; |
| }; |
| |
| // Abstraction of a register reference in MachineOperand. It contains the |
| // register number and the subregister index. |
| // FIXME: Consolidate duplicate definitions of RegisterRef |
| struct BitTracker::RegisterRef { |
| RegisterRef(Register R = 0, unsigned S = 0) : Reg(R), Sub(S) {} |
| RegisterRef(const MachineOperand &MO) |
| : Reg(MO.getReg()), Sub(MO.getSubReg()) {} |
| |
| Register Reg; |
| unsigned Sub; |
| }; |
| |
| // Value that a single bit can take. This is outside of the context of |
| // any register, it is more of an abstraction of the two-element set of |
| // possible bit values. One extension here is the "Ref" type, which |
| // indicates that this bit takes the same value as the bit described by |
| // RefInfo. |
| struct BitTracker::BitValue { |
| enum ValueType { |
| Top, // Bit not yet defined. |
| Zero, // Bit = 0. |
| One, // Bit = 1. |
| Ref // Bit value same as the one described in RefI. |
| // Conceptually, there is no explicit "bottom" value: the lattice's |
| // bottom will be expressed as a "ref to itself", which, in the context |
| // of registers, could be read as "this value of this bit is defined by |
| // this bit". |
| // The ordering is: |
| // x <= Top, |
| // Self <= x, where "Self" is "ref to itself". |
| // This makes the value lattice different for each virtual register |
| // (even for each bit in the same virtual register), since the "bottom" |
| // for one register will be a simple "ref" for another register. |
| // Since we do not store the "Self" bit and register number, the meet |
| // operation will need to take it as a parameter. |
| // |
| // In practice there is a special case for values that are not associa- |
| // ted with any specific virtual register. An example would be a value |
| // corresponding to a bit of a physical register, or an intermediate |
| // value obtained in some computation (such as instruction evaluation). |
| // Such cases are identical to the usual Ref type, but the register |
| // number is 0. In such case the Pos field of the reference is ignored. |
| // |
| // What is worthy of notice is that in value V (that is a "ref"), as long |
| // as the RefI.Reg is not 0, it may actually be the same register as the |
| // one in which V will be contained. If the RefI.Pos refers to the posi- |
| // tion of V, then V is assumed to be "bottom" (as a "ref to itself"), |
| // otherwise V is taken to be identical to the referenced bit of the |
| // same register. |
| // If RefI.Reg is 0, however, such a reference to the same register is |
| // not possible. Any value V that is a "ref", and whose RefI.Reg is 0 |
| // is treated as "bottom". |
| }; |
| ValueType Type; |
| BitRef RefI; |
| |
| BitValue(ValueType T = Top) : Type(T) {} |
| BitValue(bool B) : Type(B ? One : Zero) {} |
| BitValue(unsigned Reg, uint16_t Pos) : Type(Ref), RefI(Reg, Pos) {} |
| |
| bool operator== (const BitValue &V) const { |
| if (Type != V.Type) |
| return false; |
| if (Type == Ref && !(RefI == V.RefI)) |
| return false; |
| return true; |
| } |
| bool operator!= (const BitValue &V) const { |
| return !operator==(V); |
| } |
| |
| bool is(unsigned T) const { |
| assert(T == 0 || T == 1); |
| return T == 0 ? Type == Zero |
| : (T == 1 ? Type == One : false); |
| } |
| |
| // The "meet" operation is the "." operation in a semilattice (L, ., T, B): |
| // (1) x.x = x |
| // (2) x.y = y.x |
| // (3) x.(y.z) = (x.y).z |
| // (4) x.T = x (i.e. T = "top") |
| // (5) x.B = B (i.e. B = "bottom") |
| // |
| // This "meet" function will update the value of the "*this" object with |
| // the newly calculated one, and return "true" if the value of *this has |
| // changed, and "false" otherwise. |
| // To prove that it satisfies the conditions (1)-(5), it is sufficient |
| // to show that a relation |
| // x <= y <=> x.y = x |
| // defines a partial order (i.e. that "meet" is same as "infimum"). |
| bool meet(const BitValue &V, const BitRef &Self) { |
| // First, check the cases where there is nothing to be done. |
| if (Type == Ref && RefI == Self) // Bottom.meet(V) = Bottom (i.e. This) |
| return false; |
| if (V.Type == Top) // This.meet(Top) = This |
| return false; |
| if (*this == V) // This.meet(This) = This |
| return false; |
| |
| // At this point, we know that the value of "this" will change. |
| // If it is Top, it will become the same as V, otherwise it will |
| // become "bottom" (i.e. Self). |
| if (Type == Top) { |
| Type = V.Type; |
| RefI = V.RefI; // This may be irrelevant, but copy anyway. |
| return true; |
| } |
| // Become "bottom". |
| Type = Ref; |
| RefI = Self; |
| return true; |
| } |
| |
| // Create a reference to the bit value V. |
| static BitValue ref(const BitValue &V); |
| // Create a "self". |
| static BitValue self(const BitRef &Self = BitRef()); |
| |
| bool num() const { |
| return Type == Zero || Type == One; |
| } |
| |
| operator bool() const { |
| assert(Type == Zero || Type == One); |
| return Type == One; |
| } |
| |
| friend raw_ostream &operator<<(raw_ostream &OS, const BitValue &BV); |
| }; |
| |
| // This operation must be idempotent, i.e. ref(ref(V)) == ref(V). |
| inline BitTracker::BitValue |
| BitTracker::BitValue::ref(const BitValue &V) { |
| if (V.Type != Ref) |
| return BitValue(V.Type); |
| if (V.RefI.Reg != 0) |
| return BitValue(V.RefI.Reg, V.RefI.Pos); |
| return self(); |
| } |
| |
| inline BitTracker::BitValue |
| BitTracker::BitValue::self(const BitRef &Self) { |
| return BitValue(Self.Reg, Self.Pos); |
| } |
| |
| // A sequence of bits starting from index B up to and including index E. |
| // If E < B, the mask represents two sections: [0..E] and [B..W) where |
| // W is the width of the register. |
| struct BitTracker::BitMask { |
| BitMask() = default; |
| BitMask(uint16_t b, uint16_t e) : B(b), E(e) {} |
| |
| uint16_t first() const { return B; } |
| uint16_t last() const { return E; } |
| |
| private: |
| uint16_t B = 0; |
| uint16_t E = 0; |
| }; |
| |
| // Representation of a register: a list of BitValues. |
| struct BitTracker::RegisterCell { |
| RegisterCell(uint16_t Width = DefaultBitN) : Bits(Width) {} |
| |
| uint16_t width() const { |
| return Bits.size(); |
| } |
| |
| const BitValue &operator[](uint16_t BitN) const { |
| assert(BitN < Bits.size()); |
| return Bits[BitN]; |
| } |
| BitValue &operator[](uint16_t BitN) { |
| assert(BitN < Bits.size()); |
| return Bits[BitN]; |
| } |
| |
| bool meet(const RegisterCell &RC, Register SelfR); |
| RegisterCell &insert(const RegisterCell &RC, const BitMask &M); |
| RegisterCell extract(const BitMask &M) const; // Returns a new cell. |
| RegisterCell &rol(uint16_t Sh); // Rotate left. |
| RegisterCell &fill(uint16_t B, uint16_t E, const BitValue &V); |
| RegisterCell &cat(const RegisterCell &RC); // Concatenate. |
| uint16_t cl(bool B) const; |
| uint16_t ct(bool B) const; |
| |
| bool operator== (const RegisterCell &RC) const; |
| bool operator!= (const RegisterCell &RC) const { |
| return !operator==(RC); |
| } |
| |
| // Replace the ref-to-reg-0 bit values with the given register. |
| RegisterCell ®ify(unsigned R); |
| |
| // Generate a "ref" cell for the corresponding register. In the resulting |
| // cell each bit will be described as being the same as the corresponding |
| // bit in register Reg (i.e. the cell is "defined" by register Reg). |
| static RegisterCell self(unsigned Reg, uint16_t Width); |
| // Generate a "top" cell of given size. |
| static RegisterCell top(uint16_t Width); |
| // Generate a cell that is a "ref" to another cell. |
| static RegisterCell ref(const RegisterCell &C); |
| |
| private: |
| // The DefaultBitN is here only to avoid frequent reallocation of the |
| // memory in the vector. |
| static const unsigned DefaultBitN = 32; |
| using BitValueList = SmallVector<BitValue, DefaultBitN>; |
| BitValueList Bits; |
| |
| friend raw_ostream &operator<<(raw_ostream &OS, const RegisterCell &RC); |
| }; |
| |
| inline bool BitTracker::has(unsigned Reg) const { |
| return Map.find(Reg) != Map.end(); |
| } |
| |
| inline const BitTracker::RegisterCell& |
| BitTracker::lookup(unsigned Reg) const { |
| CellMapType::const_iterator F = Map.find(Reg); |
| assert(F != Map.end()); |
| return F->second; |
| } |
| |
| inline BitTracker::RegisterCell |
| BitTracker::RegisterCell::self(unsigned Reg, uint16_t Width) { |
| RegisterCell RC(Width); |
| for (uint16_t i = 0; i < Width; ++i) |
| RC.Bits[i] = BitValue::self(BitRef(Reg, i)); |
| return RC; |
| } |
| |
| inline BitTracker::RegisterCell |
| BitTracker::RegisterCell::top(uint16_t Width) { |
| RegisterCell RC(Width); |
| for (uint16_t i = 0; i < Width; ++i) |
| RC.Bits[i] = BitValue(BitValue::Top); |
| return RC; |
| } |
| |
| inline BitTracker::RegisterCell |
| BitTracker::RegisterCell::ref(const RegisterCell &C) { |
| uint16_t W = C.width(); |
| RegisterCell RC(W); |
| for (unsigned i = 0; i < W; ++i) |
| RC[i] = BitValue::ref(C[i]); |
| return RC; |
| } |
| |
| // A class to evaluate target's instructions and update the cell maps. |
| // This is used internally by the bit tracker. A target that wants to |
| // utilize this should implement the evaluation functions (noted below) |
| // in a subclass of this class. |
| struct BitTracker::MachineEvaluator { |
| MachineEvaluator(const TargetRegisterInfo &T, MachineRegisterInfo &M) |
| : TRI(T), MRI(M) {} |
| virtual ~MachineEvaluator() = default; |
| |
| uint16_t getRegBitWidth(const RegisterRef &RR) const; |
| |
| RegisterCell getCell(const RegisterRef &RR, const CellMapType &M) const; |
| void putCell(const RegisterRef &RR, RegisterCell RC, CellMapType &M) const; |
| |
| // A result of any operation should use refs to the source cells, not |
| // the cells directly. This function is a convenience wrapper to quickly |
| // generate a ref for a cell corresponding to a register reference. |
| RegisterCell getRef(const RegisterRef &RR, const CellMapType &M) const { |
| RegisterCell RC = getCell(RR, M); |
| return RegisterCell::ref(RC); |
| } |
| |
| // Helper functions. |
| // Check if a cell is an immediate value (i.e. all bits are either 0 or 1). |
| bool isInt(const RegisterCell &A) const; |
| // Convert cell to an immediate value. |
| uint64_t toInt(const RegisterCell &A) const; |
| |
| // Generate cell from an immediate value. |
| RegisterCell eIMM(int64_t V, uint16_t W) const; |
| RegisterCell eIMM(const ConstantInt *CI) const; |
| |
| // Arithmetic. |
| RegisterCell eADD(const RegisterCell &A1, const RegisterCell &A2) const; |
| RegisterCell eSUB(const RegisterCell &A1, const RegisterCell &A2) const; |
| RegisterCell eMLS(const RegisterCell &A1, const RegisterCell &A2) const; |
| RegisterCell eMLU(const RegisterCell &A1, const RegisterCell &A2) const; |
| |
| // Shifts. |
| RegisterCell eASL(const RegisterCell &A1, uint16_t Sh) const; |
| RegisterCell eLSR(const RegisterCell &A1, uint16_t Sh) const; |
| RegisterCell eASR(const RegisterCell &A1, uint16_t Sh) const; |
| |
| // Logical. |
| RegisterCell eAND(const RegisterCell &A1, const RegisterCell &A2) const; |
| RegisterCell eORL(const RegisterCell &A1, const RegisterCell &A2) const; |
| RegisterCell eXOR(const RegisterCell &A1, const RegisterCell &A2) const; |
| RegisterCell eNOT(const RegisterCell &A1) const; |
| |
| // Set bit, clear bit. |
| RegisterCell eSET(const RegisterCell &A1, uint16_t BitN) const; |
| RegisterCell eCLR(const RegisterCell &A1, uint16_t BitN) const; |
| |
| // Count leading/trailing bits (zeros/ones). |
| RegisterCell eCLB(const RegisterCell &A1, bool B, uint16_t W) const; |
| RegisterCell eCTB(const RegisterCell &A1, bool B, uint16_t W) const; |
| |
| // Sign/zero extension. |
| RegisterCell eSXT(const RegisterCell &A1, uint16_t FromN) const; |
| RegisterCell eZXT(const RegisterCell &A1, uint16_t FromN) const; |
| |
| // Extract/insert |
| // XTR R,b,e: extract bits from A1 starting at bit b, ending at e-1. |
| // INS R,S,b: take R and replace bits starting from b with S. |
| RegisterCell eXTR(const RegisterCell &A1, uint16_t B, uint16_t E) const; |
| RegisterCell eINS(const RegisterCell &A1, const RegisterCell &A2, |
| uint16_t AtN) const; |
| |
| // User-provided functions for individual targets: |
| |
| // Return a sub-register mask that indicates which bits in Reg belong |
| // to the subregister Sub. These bits are assumed to be contiguous in |
| // the super-register, and have the same ordering in the sub-register |
| // as in the super-register. It is valid to call this function with |
| // Sub == 0, in this case, the function should return a mask that spans |
| // the entire register Reg (which is what the default implementation |
| // does). |
| virtual BitMask mask(Register Reg, unsigned Sub) const; |
| // Indicate whether a given register class should be tracked. |
| virtual bool track(const TargetRegisterClass *RC) const { return true; } |
| // Evaluate a non-branching machine instruction, given the cell map with |
| // the input values. Place the results in the Outputs map. Return "true" |
| // if evaluation succeeded, "false" otherwise. |
| virtual bool evaluate(const MachineInstr &MI, const CellMapType &Inputs, |
| CellMapType &Outputs) const; |
| // Evaluate a branch, given the cell map with the input values. Fill out |
| // a list of all possible branch targets and indicate (through a flag) |
| // whether the branch could fall-through. Return "true" if this information |
| // has been successfully computed, "false" otherwise. |
| virtual bool evaluate(const MachineInstr &BI, const CellMapType &Inputs, |
| BranchTargetList &Targets, bool &FallsThru) const = 0; |
| // Given a register class RC, return a register class that should be assumed |
| // when a register from class RC is used with a subregister of index Idx. |
| virtual const TargetRegisterClass& |
| composeWithSubRegIndex(const TargetRegisterClass &RC, unsigned Idx) const { |
| if (Idx == 0) |
| return RC; |
| llvm_unreachable("Unimplemented composeWithSubRegIndex"); |
| } |
| // Return the size in bits of the physical register Reg. |
| virtual uint16_t getPhysRegBitWidth(MCRegister Reg) const; |
| |
| const TargetRegisterInfo &TRI; |
| MachineRegisterInfo &MRI; |
| }; |
| |
| } // end namespace llvm |
| |
| #endif // LLVM_LIB_TARGET_HEXAGON_BITTRACKER_H |