544 lines
16 KiB
C++
544 lines
16 KiB
C++
//===- HexagonGenPredicate.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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "HexagonInstrInfo.h"
|
|
#include "HexagonSubtarget.h"
|
|
#include "llvm/ADT/SetVector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/CodeGen/MachineBasicBlock.h"
|
|
#include "llvm/CodeGen/MachineDominators.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/IR/DebugLoc.h"
|
|
#include "llvm/InitializePasses.h"
|
|
#include "llvm/Pass.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <cassert>
|
|
#include <iterator>
|
|
#include <map>
|
|
#include <queue>
|
|
#include <set>
|
|
#include <utility>
|
|
|
|
#define DEBUG_TYPE "gen-pred"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace llvm {
|
|
|
|
void initializeHexagonGenPredicatePass(PassRegistry& Registry);
|
|
FunctionPass *createHexagonGenPredicate();
|
|
|
|
} // end namespace llvm
|
|
|
|
namespace {
|
|
|
|
// FIXME: Use TargetInstrInfo::RegSubRegPair
|
|
struct RegisterSubReg {
|
|
Register R;
|
|
unsigned S;
|
|
|
|
RegisterSubReg(unsigned r = 0, unsigned s = 0) : R(r), S(s) {}
|
|
RegisterSubReg(const MachineOperand &MO) : R(MO.getReg()), S(MO.getSubReg()) {}
|
|
RegisterSubReg(const Register &Reg) : R(Reg), S(0) {}
|
|
|
|
bool operator== (const RegisterSubReg &Reg) const {
|
|
return R == Reg.R && S == Reg.S;
|
|
}
|
|
|
|
bool operator< (const RegisterSubReg &Reg) const {
|
|
return R < Reg.R || (R == Reg.R && S < Reg.S);
|
|
}
|
|
};
|
|
|
|
struct PrintRegister {
|
|
friend raw_ostream &operator<< (raw_ostream &OS, const PrintRegister &PR);
|
|
|
|
PrintRegister(RegisterSubReg R, const TargetRegisterInfo &I) : Reg(R), TRI(I) {}
|
|
|
|
private:
|
|
RegisterSubReg Reg;
|
|
const TargetRegisterInfo &TRI;
|
|
};
|
|
|
|
raw_ostream &operator<< (raw_ostream &OS, const PrintRegister &PR)
|
|
LLVM_ATTRIBUTE_UNUSED;
|
|
raw_ostream &operator<< (raw_ostream &OS, const PrintRegister &PR) {
|
|
return OS << printReg(PR.Reg.R, &PR.TRI, PR.Reg.S);
|
|
}
|
|
|
|
class HexagonGenPredicate : public MachineFunctionPass {
|
|
public:
|
|
static char ID;
|
|
|
|
HexagonGenPredicate() : MachineFunctionPass(ID) {
|
|
initializeHexagonGenPredicatePass(*PassRegistry::getPassRegistry());
|
|
}
|
|
|
|
StringRef getPassName() const override {
|
|
return "Hexagon generate predicate operations";
|
|
}
|
|
|
|
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
|
AU.addRequired<MachineDominatorTree>();
|
|
AU.addPreserved<MachineDominatorTree>();
|
|
MachineFunctionPass::getAnalysisUsage(AU);
|
|
}
|
|
|
|
bool runOnMachineFunction(MachineFunction &MF) override;
|
|
|
|
private:
|
|
using VectOfInst = SetVector<MachineInstr *>;
|
|
using SetOfReg = std::set<RegisterSubReg>;
|
|
using RegToRegMap = std::map<RegisterSubReg, RegisterSubReg>;
|
|
|
|
const HexagonInstrInfo *TII = nullptr;
|
|
const HexagonRegisterInfo *TRI = nullptr;
|
|
MachineRegisterInfo *MRI = nullptr;
|
|
SetOfReg PredGPRs;
|
|
VectOfInst PUsers;
|
|
RegToRegMap G2P;
|
|
|
|
bool isPredReg(Register R);
|
|
void collectPredicateGPR(MachineFunction &MF);
|
|
void processPredicateGPR(const RegisterSubReg &Reg);
|
|
unsigned getPredForm(unsigned Opc);
|
|
bool isConvertibleToPredForm(const MachineInstr *MI);
|
|
bool isScalarCmp(unsigned Opc);
|
|
bool isScalarPred(RegisterSubReg PredReg);
|
|
RegisterSubReg getPredRegFor(const RegisterSubReg &Reg);
|
|
bool convertToPredForm(MachineInstr *MI);
|
|
bool eliminatePredCopies(MachineFunction &MF);
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
char HexagonGenPredicate::ID = 0;
|
|
|
|
INITIALIZE_PASS_BEGIN(HexagonGenPredicate, "hexagon-gen-pred",
|
|
"Hexagon generate predicate operations", false, false)
|
|
INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree)
|
|
INITIALIZE_PASS_END(HexagonGenPredicate, "hexagon-gen-pred",
|
|
"Hexagon generate predicate operations", false, false)
|
|
|
|
bool HexagonGenPredicate::isPredReg(Register R) {
|
|
if (!R.isVirtual())
|
|
return false;
|
|
const TargetRegisterClass *RC = MRI->getRegClass(R);
|
|
return RC == &Hexagon::PredRegsRegClass;
|
|
}
|
|
|
|
unsigned HexagonGenPredicate::getPredForm(unsigned Opc) {
|
|
using namespace Hexagon;
|
|
|
|
switch (Opc) {
|
|
case A2_and:
|
|
case A2_andp:
|
|
return C2_and;
|
|
case A4_andn:
|
|
case A4_andnp:
|
|
return C2_andn;
|
|
case M4_and_and:
|
|
return C4_and_and;
|
|
case M4_and_andn:
|
|
return C4_and_andn;
|
|
case M4_and_or:
|
|
return C4_and_or;
|
|
|
|
case A2_or:
|
|
case A2_orp:
|
|
return C2_or;
|
|
case A4_orn:
|
|
case A4_ornp:
|
|
return C2_orn;
|
|
case M4_or_and:
|
|
return C4_or_and;
|
|
case M4_or_andn:
|
|
return C4_or_andn;
|
|
case M4_or_or:
|
|
return C4_or_or;
|
|
|
|
case A2_xor:
|
|
case A2_xorp:
|
|
return C2_xor;
|
|
|
|
case C2_tfrrp:
|
|
return COPY;
|
|
}
|
|
// The opcode corresponding to 0 is TargetOpcode::PHI. We can use 0 here
|
|
// to denote "none", but we need to make sure that none of the valid opcodes
|
|
// that we return will ever be 0.
|
|
static_assert(PHI == 0, "Use different value for <none>");
|
|
return 0;
|
|
}
|
|
|
|
bool HexagonGenPredicate::isConvertibleToPredForm(const MachineInstr *MI) {
|
|
unsigned Opc = MI->getOpcode();
|
|
if (getPredForm(Opc) != 0)
|
|
return true;
|
|
|
|
// Comparisons against 0 are also convertible. This does not apply to
|
|
// A4_rcmpeqi or A4_rcmpneqi, since they produce values 0 or 1, which
|
|
// may not match the value that the predicate register would have if
|
|
// it was converted to a predicate form.
|
|
switch (Opc) {
|
|
case Hexagon::C2_cmpeqi:
|
|
case Hexagon::C4_cmpneqi:
|
|
if (MI->getOperand(2).isImm() && MI->getOperand(2).getImm() == 0)
|
|
return true;
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void HexagonGenPredicate::collectPredicateGPR(MachineFunction &MF) {
|
|
for (MachineFunction::iterator A = MF.begin(), Z = MF.end(); A != Z; ++A) {
|
|
MachineBasicBlock &B = *A;
|
|
for (MachineBasicBlock::iterator I = B.begin(), E = B.end(); I != E; ++I) {
|
|
MachineInstr *MI = &*I;
|
|
unsigned Opc = MI->getOpcode();
|
|
switch (Opc) {
|
|
case Hexagon::C2_tfrpr:
|
|
case TargetOpcode::COPY:
|
|
if (isPredReg(MI->getOperand(1).getReg())) {
|
|
RegisterSubReg RD = MI->getOperand(0);
|
|
if (RD.R.isVirtual())
|
|
PredGPRs.insert(RD);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void HexagonGenPredicate::processPredicateGPR(const RegisterSubReg &Reg) {
|
|
LLVM_DEBUG(dbgs() << __func__ << ": " << printReg(Reg.R, TRI, Reg.S) << "\n");
|
|
using use_iterator = MachineRegisterInfo::use_iterator;
|
|
|
|
use_iterator I = MRI->use_begin(Reg.R), E = MRI->use_end();
|
|
if (I == E) {
|
|
LLVM_DEBUG(dbgs() << "Dead reg: " << printReg(Reg.R, TRI, Reg.S) << '\n');
|
|
MachineInstr *DefI = MRI->getVRegDef(Reg.R);
|
|
DefI->eraseFromParent();
|
|
return;
|
|
}
|
|
|
|
for (; I != E; ++I) {
|
|
MachineInstr *UseI = I->getParent();
|
|
if (isConvertibleToPredForm(UseI))
|
|
PUsers.insert(UseI);
|
|
}
|
|
}
|
|
|
|
RegisterSubReg HexagonGenPredicate::getPredRegFor(const RegisterSubReg &Reg) {
|
|
// Create a predicate register for a given Reg. The newly created register
|
|
// will have its value copied from Reg, so that it can be later used as
|
|
// an operand in other instructions.
|
|
assert(Reg.R.isVirtual());
|
|
RegToRegMap::iterator F = G2P.find(Reg);
|
|
if (F != G2P.end())
|
|
return F->second;
|
|
|
|
LLVM_DEBUG(dbgs() << __func__ << ": " << PrintRegister(Reg, *TRI));
|
|
MachineInstr *DefI = MRI->getVRegDef(Reg.R);
|
|
assert(DefI);
|
|
unsigned Opc = DefI->getOpcode();
|
|
if (Opc == Hexagon::C2_tfrpr || Opc == TargetOpcode::COPY) {
|
|
assert(DefI->getOperand(0).isDef() && DefI->getOperand(1).isUse());
|
|
RegisterSubReg PR = DefI->getOperand(1);
|
|
G2P.insert(std::make_pair(Reg, PR));
|
|
LLVM_DEBUG(dbgs() << " -> " << PrintRegister(PR, *TRI) << '\n');
|
|
return PR;
|
|
}
|
|
|
|
MachineBasicBlock &B = *DefI->getParent();
|
|
DebugLoc DL = DefI->getDebugLoc();
|
|
const TargetRegisterClass *PredRC = &Hexagon::PredRegsRegClass;
|
|
Register NewPR = MRI->createVirtualRegister(PredRC);
|
|
|
|
// For convertible instructions, do not modify them, so that they can
|
|
// be converted later. Generate a copy from Reg to NewPR.
|
|
if (isConvertibleToPredForm(DefI)) {
|
|
MachineBasicBlock::iterator DefIt = DefI;
|
|
BuildMI(B, std::next(DefIt), DL, TII->get(TargetOpcode::COPY), NewPR)
|
|
.addReg(Reg.R, 0, Reg.S);
|
|
G2P.insert(std::make_pair(Reg, RegisterSubReg(NewPR)));
|
|
LLVM_DEBUG(dbgs() << " -> !" << PrintRegister(RegisterSubReg(NewPR), *TRI)
|
|
<< '\n');
|
|
return RegisterSubReg(NewPR);
|
|
}
|
|
|
|
llvm_unreachable("Invalid argument");
|
|
}
|
|
|
|
bool HexagonGenPredicate::isScalarCmp(unsigned Opc) {
|
|
switch (Opc) {
|
|
case Hexagon::C2_cmpeq:
|
|
case Hexagon::C2_cmpgt:
|
|
case Hexagon::C2_cmpgtu:
|
|
case Hexagon::C2_cmpeqp:
|
|
case Hexagon::C2_cmpgtp:
|
|
case Hexagon::C2_cmpgtup:
|
|
case Hexagon::C2_cmpeqi:
|
|
case Hexagon::C2_cmpgti:
|
|
case Hexagon::C2_cmpgtui:
|
|
case Hexagon::C2_cmpgei:
|
|
case Hexagon::C2_cmpgeui:
|
|
case Hexagon::C4_cmpneqi:
|
|
case Hexagon::C4_cmpltei:
|
|
case Hexagon::C4_cmplteui:
|
|
case Hexagon::C4_cmpneq:
|
|
case Hexagon::C4_cmplte:
|
|
case Hexagon::C4_cmplteu:
|
|
case Hexagon::A4_cmpbeq:
|
|
case Hexagon::A4_cmpbeqi:
|
|
case Hexagon::A4_cmpbgtu:
|
|
case Hexagon::A4_cmpbgtui:
|
|
case Hexagon::A4_cmpbgt:
|
|
case Hexagon::A4_cmpbgti:
|
|
case Hexagon::A4_cmpheq:
|
|
case Hexagon::A4_cmphgt:
|
|
case Hexagon::A4_cmphgtu:
|
|
case Hexagon::A4_cmpheqi:
|
|
case Hexagon::A4_cmphgti:
|
|
case Hexagon::A4_cmphgtui:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool HexagonGenPredicate::isScalarPred(RegisterSubReg PredReg) {
|
|
std::queue<RegisterSubReg> WorkQ;
|
|
WorkQ.push(PredReg);
|
|
|
|
while (!WorkQ.empty()) {
|
|
RegisterSubReg PR = WorkQ.front();
|
|
WorkQ.pop();
|
|
const MachineInstr *DefI = MRI->getVRegDef(PR.R);
|
|
if (!DefI)
|
|
return false;
|
|
unsigned DefOpc = DefI->getOpcode();
|
|
switch (DefOpc) {
|
|
case TargetOpcode::COPY: {
|
|
const TargetRegisterClass *PredRC = &Hexagon::PredRegsRegClass;
|
|
if (MRI->getRegClass(PR.R) != PredRC)
|
|
return false;
|
|
// If it is a copy between two predicate registers, fall through.
|
|
LLVM_FALLTHROUGH;
|
|
}
|
|
case Hexagon::C2_and:
|
|
case Hexagon::C2_andn:
|
|
case Hexagon::C4_and_and:
|
|
case Hexagon::C4_and_andn:
|
|
case Hexagon::C4_and_or:
|
|
case Hexagon::C2_or:
|
|
case Hexagon::C2_orn:
|
|
case Hexagon::C4_or_and:
|
|
case Hexagon::C4_or_andn:
|
|
case Hexagon::C4_or_or:
|
|
case Hexagon::C4_or_orn:
|
|
case Hexagon::C2_xor:
|
|
// Add operands to the queue.
|
|
for (const MachineOperand &MO : DefI->operands())
|
|
if (MO.isReg() && MO.isUse())
|
|
WorkQ.push(RegisterSubReg(MO.getReg()));
|
|
break;
|
|
|
|
// All non-vector compares are ok, everything else is bad.
|
|
default:
|
|
return isScalarCmp(DefOpc);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool HexagonGenPredicate::convertToPredForm(MachineInstr *MI) {
|
|
LLVM_DEBUG(dbgs() << __func__ << ": " << MI << " " << *MI);
|
|
|
|
unsigned Opc = MI->getOpcode();
|
|
assert(isConvertibleToPredForm(MI));
|
|
unsigned NumOps = MI->getNumOperands();
|
|
for (unsigned i = 0; i < NumOps; ++i) {
|
|
MachineOperand &MO = MI->getOperand(i);
|
|
if (!MO.isReg() || !MO.isUse())
|
|
continue;
|
|
RegisterSubReg Reg(MO);
|
|
if (Reg.S && Reg.S != Hexagon::isub_lo)
|
|
return false;
|
|
if (!PredGPRs.count(Reg))
|
|
return false;
|
|
}
|
|
|
|
MachineBasicBlock &B = *MI->getParent();
|
|
DebugLoc DL = MI->getDebugLoc();
|
|
|
|
unsigned NewOpc = getPredForm(Opc);
|
|
// Special case for comparisons against 0.
|
|
if (NewOpc == 0) {
|
|
switch (Opc) {
|
|
case Hexagon::C2_cmpeqi:
|
|
NewOpc = Hexagon::C2_not;
|
|
break;
|
|
case Hexagon::C4_cmpneqi:
|
|
NewOpc = TargetOpcode::COPY;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
// If it's a scalar predicate register, then all bits in it are
|
|
// the same. Otherwise, to determine whether all bits are 0 or not
|
|
// we would need to use any8.
|
|
RegisterSubReg PR = getPredRegFor(MI->getOperand(1));
|
|
if (!isScalarPred(PR))
|
|
return false;
|
|
// This will skip the immediate argument when creating the predicate
|
|
// version instruction.
|
|
NumOps = 2;
|
|
}
|
|
|
|
// Some sanity: check that def is in operand #0.
|
|
MachineOperand &Op0 = MI->getOperand(0);
|
|
assert(Op0.isDef());
|
|
RegisterSubReg OutR(Op0);
|
|
|
|
// Don't use getPredRegFor, since it will create an association between
|
|
// the argument and a created predicate register (i.e. it will insert a
|
|
// copy if a new predicate register is created).
|
|
const TargetRegisterClass *PredRC = &Hexagon::PredRegsRegClass;
|
|
RegisterSubReg NewPR = MRI->createVirtualRegister(PredRC);
|
|
MachineInstrBuilder MIB = BuildMI(B, MI, DL, TII->get(NewOpc), NewPR.R);
|
|
|
|
// Add predicate counterparts of the GPRs.
|
|
for (unsigned i = 1; i < NumOps; ++i) {
|
|
RegisterSubReg GPR = MI->getOperand(i);
|
|
RegisterSubReg Pred = getPredRegFor(GPR);
|
|
MIB.addReg(Pred.R, 0, Pred.S);
|
|
}
|
|
LLVM_DEBUG(dbgs() << "generated: " << *MIB);
|
|
|
|
// Generate a copy-out: NewGPR = NewPR, and replace all uses of OutR
|
|
// with NewGPR.
|
|
const TargetRegisterClass *RC = MRI->getRegClass(OutR.R);
|
|
Register NewOutR = MRI->createVirtualRegister(RC);
|
|
BuildMI(B, MI, DL, TII->get(TargetOpcode::COPY), NewOutR)
|
|
.addReg(NewPR.R, 0, NewPR.S);
|
|
MRI->replaceRegWith(OutR.R, NewOutR);
|
|
MI->eraseFromParent();
|
|
|
|
// If the processed instruction was C2_tfrrp (i.e. Rn = Pm; Pk = Rn),
|
|
// then the output will be a predicate register. Do not visit the
|
|
// users of it.
|
|
if (!isPredReg(NewOutR)) {
|
|
RegisterSubReg R(NewOutR);
|
|
PredGPRs.insert(R);
|
|
processPredicateGPR(R);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool HexagonGenPredicate::eliminatePredCopies(MachineFunction &MF) {
|
|
LLVM_DEBUG(dbgs() << __func__ << "\n");
|
|
const TargetRegisterClass *PredRC = &Hexagon::PredRegsRegClass;
|
|
bool Changed = false;
|
|
VectOfInst Erase;
|
|
|
|
// First, replace copies
|
|
// IntR = PredR1
|
|
// PredR2 = IntR
|
|
// with
|
|
// PredR2 = PredR1
|
|
// Such sequences can be generated when a copy-into-pred is generated from
|
|
// a gpr register holding a result of a convertible instruction. After
|
|
// the convertible instruction is converted, its predicate result will be
|
|
// copied back into the original gpr.
|
|
|
|
for (MachineBasicBlock &MBB : MF) {
|
|
for (MachineInstr &MI : MBB) {
|
|
if (MI.getOpcode() != TargetOpcode::COPY)
|
|
continue;
|
|
RegisterSubReg DR = MI.getOperand(0);
|
|
RegisterSubReg SR = MI.getOperand(1);
|
|
if (!DR.R.isVirtual())
|
|
continue;
|
|
if (!SR.R.isVirtual())
|
|
continue;
|
|
if (MRI->getRegClass(DR.R) != PredRC)
|
|
continue;
|
|
if (MRI->getRegClass(SR.R) != PredRC)
|
|
continue;
|
|
assert(!DR.S && !SR.S && "Unexpected subregister");
|
|
MRI->replaceRegWith(DR.R, SR.R);
|
|
Erase.insert(&MI);
|
|
Changed = true;
|
|
}
|
|
}
|
|
|
|
for (VectOfInst::iterator I = Erase.begin(), E = Erase.end(); I != E; ++I)
|
|
(*I)->eraseFromParent();
|
|
|
|
return Changed;
|
|
}
|
|
|
|
bool HexagonGenPredicate::runOnMachineFunction(MachineFunction &MF) {
|
|
if (skipFunction(MF.getFunction()))
|
|
return false;
|
|
|
|
TII = MF.getSubtarget<HexagonSubtarget>().getInstrInfo();
|
|
TRI = MF.getSubtarget<HexagonSubtarget>().getRegisterInfo();
|
|
MRI = &MF.getRegInfo();
|
|
PredGPRs.clear();
|
|
PUsers.clear();
|
|
G2P.clear();
|
|
|
|
bool Changed = false;
|
|
collectPredicateGPR(MF);
|
|
for (SetOfReg::iterator I = PredGPRs.begin(), E = PredGPRs.end(); I != E; ++I)
|
|
processPredicateGPR(*I);
|
|
|
|
bool Again;
|
|
do {
|
|
Again = false;
|
|
VectOfInst Processed, Copy;
|
|
|
|
using iterator = VectOfInst::iterator;
|
|
|
|
Copy = PUsers;
|
|
for (iterator I = Copy.begin(), E = Copy.end(); I != E; ++I) {
|
|
MachineInstr *MI = *I;
|
|
bool Done = convertToPredForm(MI);
|
|
if (Done) {
|
|
Processed.insert(MI);
|
|
Again = true;
|
|
}
|
|
}
|
|
Changed |= Again;
|
|
|
|
auto Done = [Processed] (MachineInstr *MI) -> bool {
|
|
return Processed.count(MI);
|
|
};
|
|
PUsers.remove_if(Done);
|
|
} while (Again);
|
|
|
|
Changed |= eliminatePredCopies(MF);
|
|
return Changed;
|
|
}
|
|
|
|
FunctionPass *llvm::createHexagonGenPredicate() {
|
|
return new HexagonGenPredicate();
|
|
}
|