#include "PPC.h"
#include "PPCSubtarget.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/CodeGen/MacroFusion.h"
#include "llvm/CodeGen/ScheduleDAGMutation.h"
using namespace llvm;
namespace {
class FusionFeature {
public:
typedef SmallDenseSet<unsigned> FusionOpSet;
enum FusionKind {
#define FUSION_KIND(KIND) FK_##KIND
#define FUSION_FEATURE(KIND, HAS_FEATURE, DEP_OP_IDX, OPSET1, OPSET2) \
FUSION_KIND(KIND),
#include "PPCMacroFusion.def"
FUSION_KIND(END)
};
private:
FusionKind Kd;
bool Supported;
int DepOpIdx;
FusionOpSet OpSet1;
FusionOpSet OpSet2;
public:
FusionFeature(FusionKind Kind, bool HasFeature, int Index,
const FusionOpSet &First, const FusionOpSet &Second) :
Kd(Kind), Supported(HasFeature), DepOpIdx(Index), OpSet1(First),
OpSet2(Second) {}
bool hasOp1(unsigned Opc) const { return OpSet1.contains(Opc); }
bool hasOp2(unsigned Opc) const { return OpSet2.contains(Opc); }
bool isSupported() const { return Supported; }
Optional<unsigned> depOpIdx() const {
if (DepOpIdx < 0)
return None;
return DepOpIdx;
}
FusionKind getKind() const { return Kd; }
};
static bool matchingRegOps(const MachineInstr &FirstMI,
int FirstMIOpIndex,
const MachineInstr &SecondMI,
int SecondMIOpIndex) {
const MachineOperand &Op1 = FirstMI.getOperand(FirstMIOpIndex);
const MachineOperand &Op2 = SecondMI.getOperand(SecondMIOpIndex);
if (!Op1.isReg() || !Op2.isReg())
return false;
return Op1.getReg() == Op2.getReg();
}
static bool matchingImmOps(const MachineInstr &MI,
int MIOpIndex,
int64_t Expect,
unsigned ExtendFrom = 64) {
const MachineOperand &Op = MI.getOperand(MIOpIndex);
if (!Op.isImm())
return false;
int64_t Imm = Op.getImm();
if (ExtendFrom < 64)
Imm = SignExtend64(Imm, ExtendFrom);
return Imm == Expect;
}
static bool checkOpConstraints(FusionFeature::FusionKind Kd,
const MachineInstr &FirstMI,
const MachineInstr &SecondMI) {
switch (Kd) {
default: return true;
case FusionFeature::FK_AddiLoad: {
const MachineOperand &RA = SecondMI.getOperand(1);
if (!RA.isReg())
return true;
return Register::isVirtualRegister(RA.getReg()) ||
(RA.getReg() != PPC::ZERO && RA.getReg() != PPC::ZERO8);
}
case FusionFeature::FK_AddisLoad: {
const MachineOperand &RT = SecondMI.getOperand(0);
if (!RT.isReg())
return true;
if (!Register::isVirtualRegister(RT.getReg()))
if (!matchingRegOps(SecondMI, 0, SecondMI, 2) ||
(RT.getReg() == PPC::ZERO || RT.getReg() == PPC::ZERO8))
return false;
const MachineOperand &SI = FirstMI.getOperand(2);
if (!SI.isImm())
return true;
int64_t Imm = SI.getImm();
if (((Imm & 0xFFF0) != 0) && ((Imm & 0xFFF0) != 0xFFF0))
return false;
if ((Imm & 0xFFF0) == 0xFFF0) {
const MachineOperand &D = SecondMI.getOperand(1);
if (!D.isImm())
return true;
int MSB = 15;
if (SecondMI.getOpcode() == PPC::LD)
MSB = 13;
return (D.getImm() & (1ULL << MSB)) == 0;
}
return true;
}
case FusionFeature::FK_SldiAdd:
return (matchingImmOps(FirstMI, 2, 3) && matchingImmOps(FirstMI, 3, 60)) ||
(matchingImmOps(FirstMI, 2, 6) && matchingImmOps(FirstMI, 3, 57));
case FusionFeature::FK_RotateLeftXor:
return matchingImmOps(FirstMI, 2, 1) && matchingImmOps(FirstMI, 3, 0);
case FusionFeature::FK_RotateRightXor:
return matchingImmOps(FirstMI, 2, 1) && matchingImmOps(FirstMI, 3, 63);
case FusionFeature::FK_LoadCmp1:
case FusionFeature::FK_LoadCmp2: {
const MachineOperand &BT = SecondMI.getOperand(0);
if (!BT.isReg() ||
(!Register::isVirtualRegister(BT.getReg()) && BT.getReg() != PPC::CR0))
return false;
if (SecondMI.getOpcode() == PPC::CMPDI &&
matchingImmOps(SecondMI, 2, -1, 16))
return true;
return matchingImmOps(SecondMI, 2, 0) || matchingImmOps(SecondMI, 2, 1);
}
case FusionFeature::FK_LoadCmp3: {
const MachineOperand &BT = SecondMI.getOperand(0);
if (!BT.isReg() ||
(!Register::isVirtualRegister(BT.getReg()) && BT.getReg() != PPC::CR0))
return false;
return matchingImmOps(SecondMI, 2, 0) || matchingImmOps(SecondMI, 2, 1) ||
matchingImmOps(SecondMI, 2, -1, 16);
}
case FusionFeature::FK_ZeroMoveCTR:
return (FirstMI.getOpcode() != PPC::MTSPR &&
FirstMI.getOpcode() != PPC::MTSPR8) ||
matchingImmOps(FirstMI, 0, 9);
case FusionFeature::FK_ZeroMoveLR:
return (FirstMI.getOpcode() != PPC::MTSPR &&
FirstMI.getOpcode() != PPC::MTSPR8) ||
matchingImmOps(FirstMI, 0, 8);
case FusionFeature::FK_AddisAddi: {
const MachineOperand &RA = FirstMI.getOperand(1);
const MachineOperand &SI = SecondMI.getOperand(2);
if (!SI.isImm() || !RA.isReg())
return false;
if (RA.getReg() == PPC::ZERO || RA.getReg() == PPC::ZERO8)
return false;
return SignExtend64(SI.getImm(), 16) >= 0;
}
case FusionFeature::FK_AddiAddis: {
const MachineOperand &RA = FirstMI.getOperand(1);
const MachineOperand &SI = FirstMI.getOperand(2);
if (!SI.isImm() || !RA.isReg())
return false;
if (RA.getReg() == PPC::ZERO || RA.getReg() == PPC::ZERO8)
return false;
int64_t ExtendedSI = SignExtend64(SI.getImm(), 16);
return ExtendedSI >= 2;
}
}
llvm_unreachable("All the cases should have been handled");
return true;
}
static bool shouldScheduleAdjacent(const TargetInstrInfo &TII,
const TargetSubtargetInfo &TSI,
const MachineInstr *FirstMI,
const MachineInstr &SecondMI) {
using namespace PPC;
const PPCSubtarget &ST = static_cast<const PPCSubtarget&>(TSI);
static const FusionFeature FusionFeatures[] = {
#define FUSION_FEATURE(KIND, HAS_FEATURE, DEP_OP_IDX, OPSET1, OPSET2) { \
FusionFeature::FUSION_KIND(KIND), ST.HAS_FEATURE(), DEP_OP_IDX, { OPSET1 },\
{ OPSET2 } },
#include "PPCMacroFusion.def"
};
#undef FUSION_KIND
for (auto &Feature : FusionFeatures) {
if (!Feature.isSupported())
continue;
if (Feature.hasOp2(SecondMI.getOpcode())) {
if (!FirstMI)
return true;
if (!Feature.hasOp1(FirstMI->getOpcode()))
continue;
auto DepOpIdx = Feature.depOpIdx();
if (DepOpIdx) {
if (!matchingRegOps(*FirstMI, 0, SecondMI, *DepOpIdx))
return false;
}
if (checkOpConstraints(Feature.getKind(), *FirstMI, SecondMI))
return true;
}
}
return false;
}
}
namespace llvm {
std::unique_ptr<ScheduleDAGMutation> createPowerPCMacroFusionDAGMutation () {
return createMacroFusionDAGMutation(shouldScheduleAdjacent);
}
}