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