//===- 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 #include #include #include #include #include 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; using CellMapType = std::map; 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; using EdgeSetType = std::set; using InstrSetType = std::set; using EdgeQueueType = std::queue; // 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 &Map) : Dist(Map) {} bool operator()(const MachineInstr *MI, const MachineInstr *MJ) const; DenseMap &Dist; }; std::priority_queue, Cmp> Uses; DenseSet Set; // Set to avoid adding duplicate entries. DenseMap 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 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; 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