#include "SystemZTargetMachine.h"
#include "SystemZISelLowering.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/KnownBits.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
#define DEBUG_TYPE "systemz-isel"
namespace {
struct SystemZAddressingMode {
enum AddrForm {
FormBD,
FormBDXNormal,
FormBDXLA,
FormBDXDynAlloc
};
AddrForm Form;
enum DispRange {
Disp12Only,
Disp12Pair,
Disp20Only,
Disp20Only128,
Disp20Pair
};
DispRange DR;
SDValue Base;
int64_t Disp;
SDValue Index;
bool IncludesDynAlloc;
SystemZAddressingMode(AddrForm form, DispRange dr)
: Form(form), DR(dr), Disp(0), IncludesDynAlloc(false) {}
bool hasIndexField() { return Form != FormBD; }
bool isDynAlloc() { return Form == FormBDXDynAlloc; }
void dump(const llvm::SelectionDAG *DAG) {
errs() << "SystemZAddressingMode " << this << '\n';
errs() << " Base ";
if (Base.getNode())
Base.getNode()->dump(DAG);
else
errs() << "null\n";
if (hasIndexField()) {
errs() << " Index ";
if (Index.getNode())
Index.getNode()->dump(DAG);
else
errs() << "null\n";
}
errs() << " Disp " << Disp;
if (IncludesDynAlloc)
errs() << " + ADJDYNALLOC";
errs() << '\n';
}
};
static uint64_t allOnes(unsigned int Count) {
assert(Count <= 64);
if (Count > 63)
return UINT64_MAX;
return (uint64_t(1) << Count) - 1;
}
struct RxSBGOperands {
RxSBGOperands(unsigned Op, SDValue N)
: Opcode(Op), BitSize(N.getValueSizeInBits()),
Mask(allOnes(BitSize)), Input(N), Start(64 - BitSize), End(63),
Rotate(0) {}
unsigned Opcode;
unsigned BitSize;
uint64_t Mask;
SDValue Input;
unsigned Start;
unsigned End;
unsigned Rotate;
};
class SystemZDAGToDAGISel : public SelectionDAGISel {
const SystemZSubtarget *Subtarget;
inline SDValue getImm(const SDNode *Node, uint64_t Imm) const {
return CurDAG->getTargetConstant(Imm, SDLoc(Node), Node->getValueType(0));
}
const SystemZTargetMachine &getTargetMachine() const {
return static_cast<const SystemZTargetMachine &>(TM);
}
const SystemZInstrInfo *getInstrInfo() const {
return Subtarget->getInstrInfo();
}
bool expandAddress(SystemZAddressingMode &AM, bool IsBase) const;
bool selectAddress(SDValue N, SystemZAddressingMode &AM) const;
void getAddressOperands(const SystemZAddressingMode &AM, EVT VT,
SDValue &Base, SDValue &Disp) const;
void getAddressOperands(const SystemZAddressingMode &AM, EVT VT,
SDValue &Base, SDValue &Disp, SDValue &Index) const;
bool selectBDAddr(SystemZAddressingMode::DispRange DR, SDValue Addr,
SDValue &Base, SDValue &Disp) const;
bool selectMVIAddr(SystemZAddressingMode::DispRange DR, SDValue Addr,
SDValue &Base, SDValue &Disp) const;
bool selectBDXAddr(SystemZAddressingMode::AddrForm Form,
SystemZAddressingMode::DispRange DR, SDValue Addr,
SDValue &Base, SDValue &Disp, SDValue &Index) const;
bool selectPCRelAddress(SDValue Addr, SDValue &Target) const {
if (SystemZISD::isPCREL(Addr.getOpcode())) {
Target = Addr.getOperand(0);
return true;
}
return false;
}
bool selectBDAddr12Only(SDValue Addr, SDValue &Base, SDValue &Disp) const {
return selectBDAddr(SystemZAddressingMode::Disp12Only, Addr, Base, Disp);
}
bool selectBDAddr12Pair(SDValue Addr, SDValue &Base, SDValue &Disp) const {
return selectBDAddr(SystemZAddressingMode::Disp12Pair, Addr, Base, Disp);
}
bool selectBDAddr20Only(SDValue Addr, SDValue &Base, SDValue &Disp) const {
return selectBDAddr(SystemZAddressingMode::Disp20Only, Addr, Base, Disp);
}
bool selectBDAddr20Pair(SDValue Addr, SDValue &Base, SDValue &Disp) const {
return selectBDAddr(SystemZAddressingMode::Disp20Pair, Addr, Base, Disp);
}
bool selectMVIAddr12Pair(SDValue Addr, SDValue &Base, SDValue &Disp) const {
return selectMVIAddr(SystemZAddressingMode::Disp12Pair, Addr, Base, Disp);
}
bool selectMVIAddr20Pair(SDValue Addr, SDValue &Base, SDValue &Disp) const {
return selectMVIAddr(SystemZAddressingMode::Disp20Pair, Addr, Base, Disp);
}
bool selectBDXAddr12Only(SDValue Addr, SDValue &Base, SDValue &Disp,
SDValue &Index) const {
return selectBDXAddr(SystemZAddressingMode::FormBDXNormal,
SystemZAddressingMode::Disp12Only,
Addr, Base, Disp, Index);
}
bool selectBDXAddr12Pair(SDValue Addr, SDValue &Base, SDValue &Disp,
SDValue &Index) const {
return selectBDXAddr(SystemZAddressingMode::FormBDXNormal,
SystemZAddressingMode::Disp12Pair,
Addr, Base, Disp, Index);
}
bool selectDynAlloc12Only(SDValue Addr, SDValue &Base, SDValue &Disp,
SDValue &Index) const {
return selectBDXAddr(SystemZAddressingMode::FormBDXDynAlloc,
SystemZAddressingMode::Disp12Only,
Addr, Base, Disp, Index);
}
bool selectBDXAddr20Only(SDValue Addr, SDValue &Base, SDValue &Disp,
SDValue &Index) const {
return selectBDXAddr(SystemZAddressingMode::FormBDXNormal,
SystemZAddressingMode::Disp20Only,
Addr, Base, Disp, Index);
}
bool selectBDXAddr20Only128(SDValue Addr, SDValue &Base, SDValue &Disp,
SDValue &Index) const {
return selectBDXAddr(SystemZAddressingMode::FormBDXNormal,
SystemZAddressingMode::Disp20Only128,
Addr, Base, Disp, Index);
}
bool selectBDXAddr20Pair(SDValue Addr, SDValue &Base, SDValue &Disp,
SDValue &Index) const {
return selectBDXAddr(SystemZAddressingMode::FormBDXNormal,
SystemZAddressingMode::Disp20Pair,
Addr, Base, Disp, Index);
}
bool selectLAAddr12Pair(SDValue Addr, SDValue &Base, SDValue &Disp,
SDValue &Index) const {
return selectBDXAddr(SystemZAddressingMode::FormBDXLA,
SystemZAddressingMode::Disp12Pair,
Addr, Base, Disp, Index);
}
bool selectLAAddr20Pair(SDValue Addr, SDValue &Base, SDValue &Disp,
SDValue &Index) const {
return selectBDXAddr(SystemZAddressingMode::FormBDXLA,
SystemZAddressingMode::Disp20Pair,
Addr, Base, Disp, Index);
}
bool selectBDVAddr12Only(SDValue Addr, SDValue Elem, SDValue &Base,
SDValue &Disp, SDValue &Index) const;
bool detectOrAndInsertion(SDValue &Op, uint64_t InsertMask) const;
bool refineRxSBGMask(RxSBGOperands &RxSBG, uint64_t Mask) const;
bool expandRxSBG(RxSBGOperands &RxSBG) const;
SDValue getUNDEF(const SDLoc &DL, EVT VT) const;
SDValue convertTo(const SDLoc &DL, EVT VT, SDValue N) const;
bool tryRISBGZero(SDNode *N);
bool tryRxSBG(SDNode *N, unsigned Opcode);
void splitLargeImmediate(unsigned Opcode, SDNode *Node, SDValue Op0,
uint64_t UpperVal, uint64_t LowerVal);
void loadVectorConstant(const SystemZVectorConstantInfo &VCI,
SDNode *Node);
bool tryGather(SDNode *N, unsigned Opcode);
bool tryScatter(StoreSDNode *Store, unsigned Opcode);
bool tryFoldLoadStoreIntoMemOperand(SDNode *Node);
bool canUseBlockOperation(StoreSDNode *Store, LoadSDNode *Load) const;
bool storeLoadCanUseMVC(SDNode *N) const;
bool storeLoadCanUseBlockBinary(SDNode *N, unsigned I) const;
bool storeLoadIsAligned(SDNode *N) const;
SDValue expandSelectBoolean(SDNode *Node);
public:
SystemZDAGToDAGISel(SystemZTargetMachine &TM, CodeGenOpt::Level OptLevel)
: SelectionDAGISel(TM, OptLevel) {}
bool runOnMachineFunction(MachineFunction &MF) override {
const Function &F = MF.getFunction();
if (F.getFnAttribute("fentry-call").getValueAsString() != "true") {
if (F.hasFnAttribute("mnop-mcount"))
report_fatal_error("mnop-mcount only supported with fentry-call");
if (F.hasFnAttribute("mrecord-mcount"))
report_fatal_error("mrecord-mcount only supported with fentry-call");
}
Subtarget = &MF.getSubtarget<SystemZSubtarget>();
return SelectionDAGISel::runOnMachineFunction(MF);
}
StringRef getPassName() const override {
return "SystemZ DAG->DAG Pattern Instruction Selection";
}
void Select(SDNode *Node) override;
bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
std::vector<SDValue> &OutOps) override;
bool IsProfitableToFold(SDValue N, SDNode *U, SDNode *Root) const override;
void PreprocessISelDAG() override;
#include "SystemZGenDAGISel.inc"
};
}
FunctionPass *llvm::createSystemZISelDag(SystemZTargetMachine &TM,
CodeGenOpt::Level OptLevel) {
return new SystemZDAGToDAGISel(TM, OptLevel);
}
static bool selectDisp(SystemZAddressingMode::DispRange DR, int64_t Val) {
switch (DR) {
case SystemZAddressingMode::Disp12Only:
return isUInt<12>(Val);
case SystemZAddressingMode::Disp12Pair:
case SystemZAddressingMode::Disp20Only:
case SystemZAddressingMode::Disp20Pair:
return isInt<20>(Val);
case SystemZAddressingMode::Disp20Only128:
return isInt<20>(Val) && isInt<20>(Val + 8);
}
llvm_unreachable("Unhandled displacement range");
}
static void changeComponent(SystemZAddressingMode &AM, bool IsBase,
SDValue Value) {
if (IsBase)
AM.Base = Value;
else
AM.Index = Value;
}
static bool expandAdjDynAlloc(SystemZAddressingMode &AM, bool IsBase,
SDValue Value) {
if (AM.isDynAlloc() && !AM.IncludesDynAlloc) {
changeComponent(AM, IsBase, Value);
AM.IncludesDynAlloc = true;
return true;
}
return false;
}
static bool expandIndex(SystemZAddressingMode &AM, SDValue Base,
SDValue Index) {
if (AM.hasIndexField() && !AM.Index.getNode()) {
AM.Base = Base;
AM.Index = Index;
return true;
}
return false;
}
static bool expandDisp(SystemZAddressingMode &AM, bool IsBase,
SDValue Op0, uint64_t Op1) {
int64_t TestDisp = AM.Disp + Op1;
if (selectDisp(AM.DR, TestDisp)) {
changeComponent(AM, IsBase, Op0);
AM.Disp = TestDisp;
return true;
}
return false;
}
bool SystemZDAGToDAGISel::expandAddress(SystemZAddressingMode &AM,
bool IsBase) const {
SDValue N = IsBase ? AM.Base : AM.Index;
unsigned Opcode = N.getOpcode();
if (Opcode == ISD::TRUNCATE) {
N = N.getOperand(0);
Opcode = N.getOpcode();
}
if (Opcode == ISD::ADD || CurDAG->isBaseWithConstantOffset(N)) {
SDValue Op0 = N.getOperand(0);
SDValue Op1 = N.getOperand(1);
unsigned Op0Code = Op0->getOpcode();
unsigned Op1Code = Op1->getOpcode();
if (Op0Code == SystemZISD::ADJDYNALLOC)
return expandAdjDynAlloc(AM, IsBase, Op1);
if (Op1Code == SystemZISD::ADJDYNALLOC)
return expandAdjDynAlloc(AM, IsBase, Op0);
if (Op0Code == ISD::Constant)
return expandDisp(AM, IsBase, Op1,
cast<ConstantSDNode>(Op0)->getSExtValue());
if (Op1Code == ISD::Constant)
return expandDisp(AM, IsBase, Op0,
cast<ConstantSDNode>(Op1)->getSExtValue());
if (IsBase && expandIndex(AM, Op0, Op1))
return true;
}
if (Opcode == SystemZISD::PCREL_OFFSET) {
SDValue Full = N.getOperand(0);
SDValue Base = N.getOperand(1);
SDValue Anchor = Base.getOperand(0);
uint64_t Offset = (cast<GlobalAddressSDNode>(Full)->getOffset() -
cast<GlobalAddressSDNode>(Anchor)->getOffset());
return expandDisp(AM, IsBase, Base, Offset);
}
return false;
}
static bool isValidDisp(SystemZAddressingMode::DispRange DR, int64_t Val) {
assert(selectDisp(DR, Val) && "Invalid displacement");
switch (DR) {
case SystemZAddressingMode::Disp12Only:
case SystemZAddressingMode::Disp20Only:
case SystemZAddressingMode::Disp20Only128:
return true;
case SystemZAddressingMode::Disp12Pair:
return isUInt<12>(Val);
case SystemZAddressingMode::Disp20Pair:
return !isUInt<12>(Val);
}
llvm_unreachable("Unhandled displacement range");
}
static bool shouldUseLA(SDNode *Base, int64_t Disp, SDNode *Index) {
if (!Base)
return false;
if (Base->getOpcode() == ISD::FrameIndex)
return true;
if (Disp) {
if (Index)
return true;
if (isUInt<12>(Disp))
return true;
if (!isInt<16>(Disp))
return true;
} else {
if (!Index)
return false;
if (Index->hasOneUse())
return false;
unsigned IndexOpcode = Index->getOpcode();
if (IndexOpcode == ISD::SIGN_EXTEND ||
IndexOpcode == ISD::SIGN_EXTEND_INREG)
return false;
}
if (Base->hasOneUse())
return false;
return true;
}
bool SystemZDAGToDAGISel::selectAddress(SDValue Addr,
SystemZAddressingMode &AM) const {
AM.Base = Addr;
if (Addr.getOpcode() == ISD::Constant &&
expandDisp(AM, true, SDValue(),
cast<ConstantSDNode>(Addr)->getSExtValue()))
;
else if (Addr.getOpcode() == SystemZISD::ADJDYNALLOC &&
expandAdjDynAlloc(AM, true, SDValue()))
;
else
while (expandAddress(AM, true) ||
(AM.Index.getNode() && expandAddress(AM, false)))
continue;
if (AM.Form == SystemZAddressingMode::FormBDXLA &&
!shouldUseLA(AM.Base.getNode(), AM.Disp, AM.Index.getNode()))
return false;
if (!isValidDisp(AM.DR, AM.Disp))
return false;
if (AM.isDynAlloc() && !AM.IncludesDynAlloc)
return false;
LLVM_DEBUG(AM.dump(CurDAG));
return true;
}
static void insertDAGNode(SelectionDAG *DAG, SDNode *Pos, SDValue N) {
if (N->getNodeId() == -1 ||
(SelectionDAGISel::getUninvalidatedNodeId(N.getNode()) >
SelectionDAGISel::getUninvalidatedNodeId(Pos))) {
DAG->RepositionNode(Pos->getIterator(), N.getNode());
N->setNodeId(Pos->getNodeId());
SelectionDAGISel::InvalidateNodeId(N.getNode());
}
}
void SystemZDAGToDAGISel::getAddressOperands(const SystemZAddressingMode &AM,
EVT VT, SDValue &Base,
SDValue &Disp) const {
Base = AM.Base;
if (!Base.getNode())
Base = CurDAG->getRegister(0, VT);
else if (Base.getOpcode() == ISD::FrameIndex) {
int64_t FrameIndex = cast<FrameIndexSDNode>(Base)->getIndex();
Base = CurDAG->getTargetFrameIndex(FrameIndex, VT);
} else if (Base.getValueType() != VT) {
assert(VT == MVT::i32 && Base.getValueType() == MVT::i64 &&
"Unexpected truncation");
SDLoc DL(Base);
SDValue Trunc = CurDAG->getNode(ISD::TRUNCATE, DL, VT, Base);
insertDAGNode(CurDAG, Base.getNode(), Trunc);
Base = Trunc;
}
Disp = CurDAG->getTargetConstant(AM.Disp, SDLoc(Base), VT);
}
void SystemZDAGToDAGISel::getAddressOperands(const SystemZAddressingMode &AM,
EVT VT, SDValue &Base,
SDValue &Disp,
SDValue &Index) const {
getAddressOperands(AM, VT, Base, Disp);
Index = AM.Index;
if (!Index.getNode())
Index = CurDAG->getRegister(0, VT);
}
bool SystemZDAGToDAGISel::selectBDAddr(SystemZAddressingMode::DispRange DR,
SDValue Addr, SDValue &Base,
SDValue &Disp) const {
SystemZAddressingMode AM(SystemZAddressingMode::FormBD, DR);
if (!selectAddress(Addr, AM))
return false;
getAddressOperands(AM, Addr.getValueType(), Base, Disp);
return true;
}
bool SystemZDAGToDAGISel::selectMVIAddr(SystemZAddressingMode::DispRange DR,
SDValue Addr, SDValue &Base,
SDValue &Disp) const {
SystemZAddressingMode AM(SystemZAddressingMode::FormBDXNormal, DR);
if (!selectAddress(Addr, AM) || AM.Index.getNode())
return false;
getAddressOperands(AM, Addr.getValueType(), Base, Disp);
return true;
}
bool SystemZDAGToDAGISel::selectBDXAddr(SystemZAddressingMode::AddrForm Form,
SystemZAddressingMode::DispRange DR,
SDValue Addr, SDValue &Base,
SDValue &Disp, SDValue &Index) const {
SystemZAddressingMode AM(Form, DR);
if (!selectAddress(Addr, AM))
return false;
getAddressOperands(AM, Addr.getValueType(), Base, Disp, Index);
return true;
}
bool SystemZDAGToDAGISel::selectBDVAddr12Only(SDValue Addr, SDValue Elem,
SDValue &Base,
SDValue &Disp,
SDValue &Index) const {
SDValue Regs[2];
if (selectBDXAddr12Only(Addr, Regs[0], Disp, Regs[1]) &&
Regs[0].getNode() && Regs[1].getNode()) {
for (unsigned int I = 0; I < 2; ++I) {
Base = Regs[I];
Index = Regs[1 - I];
if (Index.getOpcode() == ISD::ZERO_EXTEND)
Index = Index.getOperand(0);
if (Index.getOpcode() == ISD::EXTRACT_VECTOR_ELT &&
Index.getOperand(1) == Elem) {
Index = Index.getOperand(0);
return true;
}
}
}
return false;
}
bool SystemZDAGToDAGISel::detectOrAndInsertion(SDValue &Op,
uint64_t InsertMask) const {
if (Op.getOpcode() != ISD::AND)
return false;
auto *MaskNode = dyn_cast<ConstantSDNode>(Op.getOperand(1).getNode());
if (!MaskNode)
return false;
uint64_t AndMask = MaskNode->getZExtValue();
if (InsertMask & AndMask)
return false;
uint64_t Used = allOnes(Op.getValueSizeInBits());
if (Used != (AndMask | InsertMask)) {
KnownBits Known = CurDAG->computeKnownBits(Op.getOperand(0));
if (Used != (AndMask | InsertMask | Known.Zero.getZExtValue()))
return false;
}
Op = Op.getOperand(0);
return true;
}
bool SystemZDAGToDAGISel::refineRxSBGMask(RxSBGOperands &RxSBG,
uint64_t Mask) const {
const SystemZInstrInfo *TII = getInstrInfo();
if (RxSBG.Rotate != 0)
Mask = (Mask << RxSBG.Rotate) | (Mask >> (64 - RxSBG.Rotate));
Mask &= RxSBG.Mask;
if (TII->isRxSBGMask(Mask, RxSBG.BitSize, RxSBG.Start, RxSBG.End)) {
RxSBG.Mask = Mask;
return true;
}
return false;
}
static bool maskMatters(RxSBGOperands &RxSBG, uint64_t Mask) {
if (RxSBG.Rotate != 0)
Mask = ((Mask << RxSBG.Rotate) | (Mask >> (64 - RxSBG.Rotate)));
return (Mask & RxSBG.Mask) != 0;
}
bool SystemZDAGToDAGISel::expandRxSBG(RxSBGOperands &RxSBG) const {
SDValue N = RxSBG.Input;
unsigned Opcode = N.getOpcode();
switch (Opcode) {
case ISD::TRUNCATE: {
if (RxSBG.Opcode == SystemZ::RNSBG)
return false;
uint64_t BitSize = N.getValueSizeInBits();
uint64_t Mask = allOnes(BitSize);
if (!refineRxSBGMask(RxSBG, Mask))
return false;
RxSBG.Input = N.getOperand(0);
return true;
}
case ISD::AND: {
if (RxSBG.Opcode == SystemZ::RNSBG)
return false;
auto *MaskNode = dyn_cast<ConstantSDNode>(N.getOperand(1).getNode());
if (!MaskNode)
return false;
SDValue Input = N.getOperand(0);
uint64_t Mask = MaskNode->getZExtValue();
if (!refineRxSBGMask(RxSBG, Mask)) {
KnownBits Known = CurDAG->computeKnownBits(Input);
Mask |= Known.Zero.getZExtValue();
if (!refineRxSBGMask(RxSBG, Mask))
return false;
}
RxSBG.Input = Input;
return true;
}
case ISD::OR: {
if (RxSBG.Opcode != SystemZ::RNSBG)
return false;
auto *MaskNode = dyn_cast<ConstantSDNode>(N.getOperand(1).getNode());
if (!MaskNode)
return false;
SDValue Input = N.getOperand(0);
uint64_t Mask = ~MaskNode->getZExtValue();
if (!refineRxSBGMask(RxSBG, Mask)) {
KnownBits Known = CurDAG->computeKnownBits(Input);
Mask &= ~Known.One.getZExtValue();
if (!refineRxSBGMask(RxSBG, Mask))
return false;
}
RxSBG.Input = Input;
return true;
}
case ISD::ROTL: {
if (RxSBG.BitSize != 64 || N.getValueType() != MVT::i64)
return false;
auto *CountNode = dyn_cast<ConstantSDNode>(N.getOperand(1).getNode());
if (!CountNode)
return false;
RxSBG.Rotate = (RxSBG.Rotate + CountNode->getZExtValue()) & 63;
RxSBG.Input = N.getOperand(0);
return true;
}
case ISD::ANY_EXTEND:
RxSBG.Input = N.getOperand(0);
return true;
case ISD::ZERO_EXTEND:
if (RxSBG.Opcode != SystemZ::RNSBG) {
unsigned InnerBitSize = N.getOperand(0).getValueSizeInBits();
if (!refineRxSBGMask(RxSBG, allOnes(InnerBitSize)))
return false;
RxSBG.Input = N.getOperand(0);
return true;
}
LLVM_FALLTHROUGH;
case ISD::SIGN_EXTEND: {
unsigned BitSize = N.getValueSizeInBits();
unsigned InnerBitSize = N.getOperand(0).getValueSizeInBits();
if (maskMatters(RxSBG, allOnes(BitSize) - allOnes(InnerBitSize))) {
if (RxSBG.Mask == 1 && RxSBG.Rotate == 1)
RxSBG.Rotate += (BitSize - InnerBitSize);
else
return false;
}
RxSBG.Input = N.getOperand(0);
return true;
}
case ISD::SHL: {
auto *CountNode = dyn_cast<ConstantSDNode>(N.getOperand(1).getNode());
if (!CountNode)
return false;
uint64_t Count = CountNode->getZExtValue();
unsigned BitSize = N.getValueSizeInBits();
if (Count < 1 || Count >= BitSize)
return false;
if (RxSBG.Opcode == SystemZ::RNSBG) {
if (maskMatters(RxSBG, allOnes(Count)))
return false;
} else {
if (!refineRxSBGMask(RxSBG, allOnes(BitSize - Count) << Count))
return false;
}
RxSBG.Rotate = (RxSBG.Rotate + Count) & 63;
RxSBG.Input = N.getOperand(0);
return true;
}
case ISD::SRL:
case ISD::SRA: {
auto *CountNode = dyn_cast<ConstantSDNode>(N.getOperand(1).getNode());
if (!CountNode)
return false;
uint64_t Count = CountNode->getZExtValue();
unsigned BitSize = N.getValueSizeInBits();
if (Count < 1 || Count >= BitSize)
return false;
if (RxSBG.Opcode == SystemZ::RNSBG || Opcode == ISD::SRA) {
if (maskMatters(RxSBG, allOnes(Count) << (BitSize - Count)))
return false;
} else {
if (!refineRxSBGMask(RxSBG, allOnes(BitSize - Count)))
return false;
}
RxSBG.Rotate = (RxSBG.Rotate - Count) & 63;
RxSBG.Input = N.getOperand(0);
return true;
}
default:
return false;
}
}
SDValue SystemZDAGToDAGISel::getUNDEF(const SDLoc &DL, EVT VT) const {
SDNode *N = CurDAG->getMachineNode(TargetOpcode::IMPLICIT_DEF, DL, VT);
return SDValue(N, 0);
}
SDValue SystemZDAGToDAGISel::convertTo(const SDLoc &DL, EVT VT,
SDValue N) const {
if (N.getValueType() == MVT::i32 && VT == MVT::i64)
return CurDAG->getTargetInsertSubreg(SystemZ::subreg_l32,
DL, VT, getUNDEF(DL, MVT::i64), N);
if (N.getValueType() == MVT::i64 && VT == MVT::i32)
return CurDAG->getTargetExtractSubreg(SystemZ::subreg_l32, DL, VT, N);
assert(N.getValueType() == VT && "Unexpected value types");
return N;
}
bool SystemZDAGToDAGISel::tryRISBGZero(SDNode *N) {
SDLoc DL(N);
EVT VT = N->getValueType(0);
if (!VT.isInteger() || VT.getSizeInBits() > 64)
return false;
RxSBGOperands RISBG(SystemZ::RISBG, SDValue(N, 0));
unsigned Count = 0;
while (expandRxSBG(RISBG))
if (RISBG.Input.getOpcode() != ISD::ANY_EXTEND &&
RISBG.Input.getOpcode() != ISD::TRUNCATE)
Count += 1;
if (Count == 0 || isa<ConstantSDNode>(RISBG.Input))
return false;
if (Count == 1 && N->getOpcode() != ISD::AND)
return false;
if (RISBG.Rotate == 0) {
bool PreferAnd = false;
if (VT == MVT::i32)
PreferAnd = true;
else if (RISBG.Mask == 0xff ||
RISBG.Mask == 0xffff ||
RISBG.Mask == 0x7fffffff ||
SystemZ::isImmLF(~RISBG.Mask) ||
SystemZ::isImmHF(~RISBG.Mask))
PreferAnd = true;
else if (auto *Load = dyn_cast<LoadSDNode>(RISBG.Input)) {
if (Load->getMemoryVT() == MVT::i32 &&
(Load->getExtensionType() == ISD::EXTLOAD ||
Load->getExtensionType() == ISD::ZEXTLOAD) &&
RISBG.Mask == 0xffffff00 &&
Subtarget->hasLoadAndZeroRightmostByte())
PreferAnd = true;
}
if (PreferAnd) {
SDValue In = convertTo(DL, VT, RISBG.Input);
SDValue Mask = CurDAG->getConstant(RISBG.Mask, DL, VT);
SDValue New = CurDAG->getNode(ISD::AND, DL, VT, In, Mask);
if (N != New.getNode()) {
insertDAGNode(CurDAG, N, Mask);
insertDAGNode(CurDAG, N, New);
ReplaceNode(N, New.getNode());
N = New.getNode();
}
if (!N->isMachineOpcode())
SelectCode(N);
return true;
}
}
unsigned Opcode = SystemZ::RISBG;
if (Subtarget->hasMiscellaneousExtensions())
Opcode = SystemZ::RISBGN;
EVT OpcodeVT = MVT::i64;
if (VT == MVT::i32 && Subtarget->hasHighWord() &&
RISBG.Start >= 32 && RISBG.End >= RISBG.Start &&
((RISBG.Start + RISBG.Rotate) & 63) >= 32 &&
((RISBG.End + RISBG.Rotate) & 63) >=
((RISBG.Start + RISBG.Rotate) & 63)) {
Opcode = SystemZ::RISBMux;
OpcodeVT = MVT::i32;
RISBG.Start &= 31;
RISBG.End &= 31;
}
SDValue Ops[5] = {
getUNDEF(DL, OpcodeVT),
convertTo(DL, OpcodeVT, RISBG.Input),
CurDAG->getTargetConstant(RISBG.Start, DL, MVT::i32),
CurDAG->getTargetConstant(RISBG.End | 128, DL, MVT::i32),
CurDAG->getTargetConstant(RISBG.Rotate, DL, MVT::i32)
};
SDValue New = convertTo(
DL, VT, SDValue(CurDAG->getMachineNode(Opcode, DL, OpcodeVT, Ops), 0));
ReplaceNode(N, New.getNode());
return true;
}
bool SystemZDAGToDAGISel::tryRxSBG(SDNode *N, unsigned Opcode) {
SDLoc DL(N);
EVT VT = N->getValueType(0);
if (!VT.isInteger() || VT.getSizeInBits() > 64)
return false;
RxSBGOperands RxSBG[] = {
RxSBGOperands(Opcode, N->getOperand(0)),
RxSBGOperands(Opcode, N->getOperand(1))
};
unsigned Count[] = { 0, 0 };
for (unsigned I = 0; I < 2; ++I)
while (expandRxSBG(RxSBG[I]))
if (RxSBG[I].Input.getOpcode() != ISD::ANY_EXTEND &&
RxSBG[I].Input.getOpcode() != ISD::TRUNCATE)
Count[I] += 1;
if (Count[0] == 0 && Count[1] == 0)
return false;
unsigned I = Count[0] > Count[1] ? 0 : 1;
SDValue Op0 = N->getOperand(I ^ 1);
if (Opcode == SystemZ::ROSBG && (RxSBG[I].Mask & 0xff) == 0)
if (auto *Load = dyn_cast<LoadSDNode>(Op0.getNode()))
if (Load->getMemoryVT() == MVT::i8)
return false;
if (Opcode == SystemZ::ROSBG && detectOrAndInsertion(Op0, RxSBG[I].Mask)) {
Opcode = SystemZ::RISBG;
if (Subtarget->hasMiscellaneousExtensions())
Opcode = SystemZ::RISBGN;
}
SDValue Ops[5] = {
convertTo(DL, MVT::i64, Op0),
convertTo(DL, MVT::i64, RxSBG[I].Input),
CurDAG->getTargetConstant(RxSBG[I].Start, DL, MVT::i32),
CurDAG->getTargetConstant(RxSBG[I].End, DL, MVT::i32),
CurDAG->getTargetConstant(RxSBG[I].Rotate, DL, MVT::i32)
};
SDValue New = convertTo(
DL, VT, SDValue(CurDAG->getMachineNode(Opcode, DL, MVT::i64, Ops), 0));
ReplaceNode(N, New.getNode());
return true;
}
void SystemZDAGToDAGISel::splitLargeImmediate(unsigned Opcode, SDNode *Node,
SDValue Op0, uint64_t UpperVal,
uint64_t LowerVal) {
EVT VT = Node->getValueType(0);
SDLoc DL(Node);
SDValue Upper = CurDAG->getConstant(UpperVal, DL, VT);
if (Op0.getNode())
Upper = CurDAG->getNode(Opcode, DL, VT, Op0, Upper);
{
HandleSDNode Handle(Upper);
SelectCode(Upper.getNode());
Upper = Handle.getValue();
}
SDValue Lower = CurDAG->getConstant(LowerVal, DL, VT);
SDValue Or = CurDAG->getNode(Opcode, DL, VT, Upper, Lower);
ReplaceNode(Node, Or.getNode());
SelectCode(Or.getNode());
}
void SystemZDAGToDAGISel::loadVectorConstant(
const SystemZVectorConstantInfo &VCI, SDNode *Node) {
assert((VCI.Opcode == SystemZISD::BYTE_MASK ||
VCI.Opcode == SystemZISD::REPLICATE ||
VCI.Opcode == SystemZISD::ROTATE_MASK) &&
"Bad opcode!");
assert(VCI.VecVT.getSizeInBits() == 128 && "Expected a vector type");
EVT VT = Node->getValueType(0);
SDLoc DL(Node);
SmallVector<SDValue, 2> Ops;
for (unsigned OpVal : VCI.OpVals)
Ops.push_back(CurDAG->getTargetConstant(OpVal, DL, MVT::i32));
SDValue Op = CurDAG->getNode(VCI.Opcode, DL, VCI.VecVT, Ops);
if (VCI.VecVT == VT.getSimpleVT())
ReplaceNode(Node, Op.getNode());
else if (VT.getSizeInBits() == 128) {
SDValue BitCast = CurDAG->getNode(ISD::BITCAST, DL, VT, Op);
ReplaceNode(Node, BitCast.getNode());
SelectCode(BitCast.getNode());
} else { unsigned SubRegIdx =
(VT.getSizeInBits() == 32 ? SystemZ::subreg_h32 : SystemZ::subreg_h64);
ReplaceNode(
Node, CurDAG->getTargetExtractSubreg(SubRegIdx, DL, VT, Op).getNode());
}
SelectCode(Op.getNode());
}
bool SystemZDAGToDAGISel::tryGather(SDNode *N, unsigned Opcode) {
SDValue ElemV = N->getOperand(2);
auto *ElemN = dyn_cast<ConstantSDNode>(ElemV);
if (!ElemN)
return false;
unsigned Elem = ElemN->getZExtValue();
EVT VT = N->getValueType(0);
if (Elem >= VT.getVectorNumElements())
return false;
auto *Load = dyn_cast<LoadSDNode>(N->getOperand(1));
if (!Load || !Load->hasNUsesOfValue(1, 0))
return false;
if (Load->getMemoryVT().getSizeInBits() !=
Load->getValueType(0).getSizeInBits())
return false;
SDValue Base, Disp, Index;
if (!selectBDVAddr12Only(Load->getBasePtr(), ElemV, Base, Disp, Index) ||
Index.getValueType() != VT.changeVectorElementTypeToInteger())
return false;
SDLoc DL(Load);
SDValue Ops[] = {
N->getOperand(0), Base, Disp, Index,
CurDAG->getTargetConstant(Elem, DL, MVT::i32), Load->getChain()
};
SDNode *Res = CurDAG->getMachineNode(Opcode, DL, VT, MVT::Other, Ops);
ReplaceUses(SDValue(Load, 1), SDValue(Res, 1));
ReplaceNode(N, Res);
return true;
}
bool SystemZDAGToDAGISel::tryScatter(StoreSDNode *Store, unsigned Opcode) {
SDValue Value = Store->getValue();
if (Value.getOpcode() != ISD::EXTRACT_VECTOR_ELT)
return false;
if (Store->getMemoryVT().getSizeInBits() != Value.getValueSizeInBits())
return false;
SDValue ElemV = Value.getOperand(1);
auto *ElemN = dyn_cast<ConstantSDNode>(ElemV);
if (!ElemN)
return false;
SDValue Vec = Value.getOperand(0);
EVT VT = Vec.getValueType();
unsigned Elem = ElemN->getZExtValue();
if (Elem >= VT.getVectorNumElements())
return false;
SDValue Base, Disp, Index;
if (!selectBDVAddr12Only(Store->getBasePtr(), ElemV, Base, Disp, Index) ||
Index.getValueType() != VT.changeVectorElementTypeToInteger())
return false;
SDLoc DL(Store);
SDValue Ops[] = {
Vec, Base, Disp, Index, CurDAG->getTargetConstant(Elem, DL, MVT::i32),
Store->getChain()
};
ReplaceNode(Store, CurDAG->getMachineNode(Opcode, DL, MVT::Other, Ops));
return true;
}
static bool isFusableLoadOpStorePattern(StoreSDNode *StoreNode,
SDValue StoredVal, SelectionDAG *CurDAG,
LoadSDNode *&LoadNode,
SDValue &InputChain) {
if (StoredVal.getResNo() != 0)
return false;
if (!StoredVal.getNode()->hasNUsesOfValue(1, 0))
return false;
if (!ISD::isNormalStore(StoreNode) || StoreNode->isNonTemporal())
return false;
SDValue Load = StoredVal->getOperand(0);
if (!ISD::isNormalLoad(Load.getNode()))
return false;
LoadNode = cast<LoadSDNode>(Load);
if (!Load.hasOneUse())
return false;
if (LoadNode->getBasePtr() != StoreNode->getBasePtr() ||
LoadNode->getOffset() != StoreNode->getOffset())
return false;
SDValue Chain = StoreNode->getChain();
bool ChainCheck = false;
if (Chain == Load.getValue(1)) {
ChainCheck = true;
InputChain = LoadNode->getChain();
} else if (Chain.getOpcode() == ISD::TokenFactor) {
SmallVector<SDValue, 4> ChainOps;
SmallVector<const SDNode *, 4> LoopWorklist;
SmallPtrSet<const SDNode *, 16> Visited;
const unsigned int Max = 1024;
for (unsigned i = 0, e = Chain.getNumOperands(); i != e; ++i) {
SDValue Op = Chain.getOperand(i);
if (Op == Load.getValue(1)) {
ChainCheck = true;
ChainOps.push_back(Load.getOperand(0));
continue;
}
LoopWorklist.push_back(Op.getNode());
ChainOps.push_back(Op);
}
if (ChainCheck) {
for (SDValue Op : StoredVal->ops())
if (Op.getNode() != LoadNode)
LoopWorklist.push_back(Op.getNode());
if (SDNode::hasPredecessorHelper(Load.getNode(), Visited, LoopWorklist, Max,
true))
return false;
InputChain = CurDAG->getNode(ISD::TokenFactor, SDLoc(Chain),
MVT::Other, ChainOps);
}
}
if (!ChainCheck)
return false;
return true;
}
bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
StoreSDNode *StoreNode = cast<StoreSDNode>(Node);
SDValue StoredVal = StoreNode->getOperand(1);
unsigned Opc = StoredVal->getOpcode();
SDLoc DL(StoreNode);
EVT MemVT = StoreNode->getMemoryVT();
unsigned NewOpc = 0;
bool NegateOperand = false;
switch (Opc) {
default:
return false;
case SystemZISD::SSUBO:
NegateOperand = true;
LLVM_FALLTHROUGH;
case SystemZISD::SADDO:
if (MemVT == MVT::i32)
NewOpc = SystemZ::ASI;
else if (MemVT == MVT::i64)
NewOpc = SystemZ::AGSI;
else
return false;
break;
case SystemZISD::USUBO:
NegateOperand = true;
LLVM_FALLTHROUGH;
case SystemZISD::UADDO:
if (MemVT == MVT::i32)
NewOpc = SystemZ::ALSI;
else if (MemVT == MVT::i64)
NewOpc = SystemZ::ALGSI;
else
return false;
break;
}
LoadSDNode *LoadNode = nullptr;
SDValue InputChain;
if (!isFusableLoadOpStorePattern(StoreNode, StoredVal, CurDAG, LoadNode,
InputChain))
return false;
SDValue Operand = StoredVal.getOperand(1);
auto *OperandC = dyn_cast<ConstantSDNode>(Operand);
if (!OperandC)
return false;
auto OperandV = OperandC->getAPIntValue();
if (NegateOperand)
OperandV = -OperandV;
if (OperandV.getMinSignedBits() > 8)
return false;
Operand = CurDAG->getTargetConstant(OperandV, DL, MemVT);
SDValue Base, Disp;
if (!selectBDAddr20Only(StoreNode->getBasePtr(), Base, Disp))
return false;
SDValue Ops[] = { Base, Disp, Operand, InputChain };
MachineSDNode *Result =
CurDAG->getMachineNode(NewOpc, DL, MVT::i32, MVT::Other, Ops);
CurDAG->setNodeMemRefs(
Result, {StoreNode->getMemOperand(), LoadNode->getMemOperand()});
ReplaceUses(SDValue(StoreNode, 0), SDValue(Result, 1));
ReplaceUses(SDValue(StoredVal.getNode(), 1), SDValue(Result, 0));
CurDAG->RemoveDeadNode(Node);
return true;
}
bool SystemZDAGToDAGISel::canUseBlockOperation(StoreSDNode *Store,
LoadSDNode *Load) const {
if (Load->getMemoryVT() != Store->getMemoryVT())
return false;
if (Load->isVolatile() || Store->isVolatile())
return false;
if (Load->isInvariant() && Load->isDereferenceable())
return true;
const Value *V1 = Load->getMemOperand()->getValue();
const Value *V2 = Store->getMemOperand()->getValue();
if (!V1 || !V2)
return false;
uint64_t Size = Load->getMemoryVT().getStoreSize();
int64_t End1 = Load->getSrcValueOffset() + Size;
int64_t End2 = Store->getSrcValueOffset() + Size;
if (V1 == V2 && End1 == End2)
return false;
return AA->isNoAlias(MemoryLocation(V1, End1, Load->getAAInfo()),
MemoryLocation(V2, End2, Store->getAAInfo()));
}
bool SystemZDAGToDAGISel::storeLoadCanUseMVC(SDNode *N) const {
auto *Store = cast<StoreSDNode>(N);
auto *Load = cast<LoadSDNode>(Store->getValue());
uint64_t Size = Load->getMemoryVT().getStoreSize();
if (Size > 1 && Size <= 8) {
if (SystemZISD::isPCREL(Load->getBasePtr().getOpcode()))
return false;
if (SystemZISD::isPCREL(Store->getBasePtr().getOpcode()))
return false;
}
return canUseBlockOperation(Store, Load);
}
bool SystemZDAGToDAGISel::storeLoadCanUseBlockBinary(SDNode *N,
unsigned I) const {
auto *StoreA = cast<StoreSDNode>(N);
auto *LoadA = cast<LoadSDNode>(StoreA->getValue().getOperand(1 - I));
auto *LoadB = cast<LoadSDNode>(StoreA->getValue().getOperand(I));
return !LoadA->isVolatile() && LoadA->getMemoryVT() == LoadB->getMemoryVT() &&
canUseBlockOperation(StoreA, LoadB);
}
bool SystemZDAGToDAGISel::storeLoadIsAligned(SDNode *N) const {
auto *MemAccess = cast<LSBaseSDNode>(N);
TypeSize StoreSize = MemAccess->getMemoryVT().getStoreSize();
SDValue BasePtr = MemAccess->getBasePtr();
MachineMemOperand *MMO = MemAccess->getMemOperand();
assert(MMO && "Expected a memory operand.");
if (MemAccess->getAlign().value() < StoreSize ||
!MemAccess->getOffset().isUndef())
return false;
if (MMO->getOffset() % StoreSize != 0)
return false;
if (const PseudoSourceValue *PSV = MMO->getPseudoValue())
if ((PSV->isGOT() || PSV->isConstantPool()))
return true;
if (BasePtr.getNumOperands())
if (GlobalAddressSDNode *GA =
dyn_cast<GlobalAddressSDNode>(BasePtr.getOperand(0))) {
if (GA->getOffset() % StoreSize != 0)
return false;
const GlobalValue *GV = GA->getGlobal();
const DataLayout &DL = GV->getParent()->getDataLayout();
if (GV->getPointerAlignment(DL).value() < StoreSize)
return false;
}
return true;
}
void SystemZDAGToDAGISel::Select(SDNode *Node) {
if (Node->isMachineOpcode()) {
LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n");
Node->setNodeId(-1);
return;
}
unsigned Opcode = Node->getOpcode();
switch (Opcode) {
case ISD::OR:
if (Node->getOperand(1).getOpcode() != ISD::Constant)
if (tryRxSBG(Node, SystemZ::ROSBG))
return;
goto or_xor;
case ISD::XOR:
if (Node->getOperand(1).getOpcode() != ISD::Constant)
if (tryRxSBG(Node, SystemZ::RXSBG))
return;
or_xor:
if (Node->getValueType(0) == MVT::i64 &&
Node->getOperand(0).getOpcode() != ISD::Constant)
if (auto *Op1 = dyn_cast<ConstantSDNode>(Node->getOperand(1))) {
uint64_t Val = Op1->getZExtValue();
if (Subtarget->hasMiscellaneousExtensions3()) {
unsigned ChildOpcode = Node->getOperand(0).getOpcode();
if (Val == (uint64_t)-1 && Opcode == ISD::XOR)
if (ChildOpcode == ISD::AND || ChildOpcode == ISD::OR ||
ChildOpcode == ISD::XOR)
break;
if (ChildOpcode == ISD::XOR) {
auto Op0 = Node->getOperand(0);
if (auto *Op0Op1 = dyn_cast<ConstantSDNode>(Op0->getOperand(1)))
if (Op0Op1->getZExtValue() == (uint64_t)-1)
break;
}
}
if (!SystemZ::isImmLF(Val) && !SystemZ::isImmHF(Val)) {
splitLargeImmediate(Opcode, Node, Node->getOperand(0),
Val - uint32_t(Val), uint32_t(Val));
return;
}
}
break;
case ISD::AND:
if (Node->getOperand(1).getOpcode() != ISD::Constant)
if (tryRxSBG(Node, SystemZ::RNSBG))
return;
LLVM_FALLTHROUGH;
case ISD::ROTL:
case ISD::SHL:
case ISD::SRL:
case ISD::ZERO_EXTEND:
if (tryRISBGZero(Node))
return;
break;
case ISD::Constant:
if (Node->getValueType(0) == MVT::i64) {
uint64_t Val = cast<ConstantSDNode>(Node)->getZExtValue();
if (!SystemZ::isImmLF(Val) && !SystemZ::isImmHF(Val) && !isInt<32>(Val)) {
splitLargeImmediate(ISD::OR, Node, SDValue(), Val - uint32_t(Val),
uint32_t(Val));
return;
}
}
break;
case SystemZISD::SELECT_CCMASK: {
SDValue Op0 = Node->getOperand(0);
SDValue Op1 = Node->getOperand(1);
if ((Op1.getOpcode() == ISD::LOAD && Op0.getOpcode() != ISD::LOAD) ||
(Subtarget->hasLoadStoreOnCond2() &&
Node->getValueType(0).isInteger() &&
Op1.getOpcode() == ISD::Constant &&
isInt<16>(cast<ConstantSDNode>(Op1)->getSExtValue()) &&
!(Op0.getOpcode() == ISD::Constant &&
isInt<16>(cast<ConstantSDNode>(Op0)->getSExtValue())))) {
SDValue CCValid = Node->getOperand(2);
SDValue CCMask = Node->getOperand(3);
uint64_t ConstCCValid =
cast<ConstantSDNode>(CCValid.getNode())->getZExtValue();
uint64_t ConstCCMask =
cast<ConstantSDNode>(CCMask.getNode())->getZExtValue();
CCMask = CurDAG->getTargetConstant(ConstCCValid ^ ConstCCMask,
SDLoc(Node), CCMask.getValueType());
SDValue Op4 = Node->getOperand(4);
SDNode *UpdatedNode =
CurDAG->UpdateNodeOperands(Node, Op1, Op0, CCValid, CCMask, Op4);
if (UpdatedNode != Node) {
ReplaceNode(Node, UpdatedNode);
Node = UpdatedNode;
}
}
break;
}
case ISD::INSERT_VECTOR_ELT: {
EVT VT = Node->getValueType(0);
unsigned ElemBitSize = VT.getScalarSizeInBits();
if (ElemBitSize == 32) {
if (tryGather(Node, SystemZ::VGEF))
return;
} else if (ElemBitSize == 64) {
if (tryGather(Node, SystemZ::VGEG))
return;
}
break;
}
case ISD::BUILD_VECTOR: {
auto *BVN = cast<BuildVectorSDNode>(Node);
SystemZVectorConstantInfo VCI(BVN);
if (VCI.isVectorConstantLegal(*Subtarget)) {
loadVectorConstant(VCI, Node);
return;
}
break;
}
case ISD::ConstantFP: {
APFloat Imm = cast<ConstantFPSDNode>(Node)->getValueAPF();
if (Imm.isZero() || Imm.isNegZero())
break;
SystemZVectorConstantInfo VCI(Imm);
bool Success = VCI.isVectorConstantLegal(*Subtarget); (void)Success;
assert(Success && "Expected legal FP immediate");
loadVectorConstant(VCI, Node);
return;
}
case ISD::STORE: {
if (tryFoldLoadStoreIntoMemOperand(Node))
return;
auto *Store = cast<StoreSDNode>(Node);
unsigned ElemBitSize = Store->getValue().getValueSizeInBits();
if (ElemBitSize == 32) {
if (tryScatter(Store, SystemZ::VSCEF))
return;
} else if (ElemBitSize == 64) {
if (tryScatter(Store, SystemZ::VSCEG))
return;
}
break;
}
}
SelectCode(Node);
}
bool SystemZDAGToDAGISel::
SelectInlineAsmMemoryOperand(const SDValue &Op,
unsigned ConstraintID,
std::vector<SDValue> &OutOps) {
SystemZAddressingMode::AddrForm Form;
SystemZAddressingMode::DispRange DispRange;
SDValue Base, Disp, Index;
switch(ConstraintID) {
default:
llvm_unreachable("Unexpected asm memory constraint");
case InlineAsm::Constraint_i:
case InlineAsm::Constraint_Q:
case InlineAsm::Constraint_ZQ:
Form = SystemZAddressingMode::FormBD;
DispRange = SystemZAddressingMode::Disp12Only;
break;
case InlineAsm::Constraint_R:
case InlineAsm::Constraint_ZR:
Form = SystemZAddressingMode::FormBDXNormal;
DispRange = SystemZAddressingMode::Disp12Only;
break;
case InlineAsm::Constraint_S:
case InlineAsm::Constraint_ZS:
Form = SystemZAddressingMode::FormBD;
DispRange = SystemZAddressingMode::Disp20Only;
break;
case InlineAsm::Constraint_T:
case InlineAsm::Constraint_m:
case InlineAsm::Constraint_o:
case InlineAsm::Constraint_p:
case InlineAsm::Constraint_ZT:
Form = SystemZAddressingMode::FormBDXNormal;
DispRange = SystemZAddressingMode::Disp20Only;
break;
}
if (selectBDXAddr(Form, DispRange, Op, Base, Disp, Index)) {
const TargetRegisterClass *TRC =
Subtarget->getRegisterInfo()->getPointerRegClass(*MF);
SDLoc DL(Base);
SDValue RC = CurDAG->getTargetConstant(TRC->getID(), DL, MVT::i32);
if (Base.getOpcode() != ISD::TargetFrameIndex &&
Base.getOpcode() != ISD::Register) {
Base =
SDValue(CurDAG->getMachineNode(TargetOpcode::COPY_TO_REGCLASS,
DL, Base.getValueType(),
Base, RC), 0);
}
if (Index.getOpcode() != ISD::Register) {
Index =
SDValue(CurDAG->getMachineNode(TargetOpcode::COPY_TO_REGCLASS,
DL, Index.getValueType(),
Index, RC), 0);
}
OutOps.push_back(Base);
OutOps.push_back(Disp);
OutOps.push_back(Index);
return false;
}
return true;
}
bool
SystemZDAGToDAGISel::IsProfitableToFold(SDValue N, SDNode *U,
SDNode *Root) const {
if (N.getOpcode() == ISD::LOAD && U->getOpcode() == SystemZISD::ICMP) {
if (!N.hasOneUse() || !U->hasOneUse())
return false;
SDNode *CCUser = *U->use_begin();
SDNode *CCRegUser = nullptr;
if (CCUser->getOpcode() == ISD::CopyToReg ||
cast<RegisterSDNode>(CCUser->getOperand(1))->getReg() == SystemZ::CC) {
for (auto *U : CCUser->uses()) {
if (CCRegUser == nullptr)
CCRegUser = U;
else if (CCRegUser != U)
return false;
}
}
if (CCRegUser == nullptr)
return false;
if (CCRegUser->isMachineOpcode() &&
CCRegUser->getMachineOpcode() == SystemZ::BRC)
return !N->isPredecessorOf(CCUser->getOperand(0).getNode());
if (!IsLegalToFold(N, U, CCRegUser, OptLevel, false))
return false;
}
return true;
}
namespace {
struct IPMConversion {
IPMConversion(unsigned xorValue, int64_t addValue, unsigned bit)
: XORValue(xorValue), AddValue(addValue), Bit(bit) {}
int64_t XORValue;
int64_t AddValue;
unsigned Bit;
};
}
static IPMConversion getIPMConversion(unsigned CCValid, unsigned CCMask) {
if (CCMask == (CCValid & (SystemZ::CCMASK_1 | SystemZ::CCMASK_3)))
return IPMConversion(0, 0, SystemZ::IPM_CC);
if (CCMask == (CCValid & (SystemZ::CCMASK_2 | SystemZ::CCMASK_3)))
return IPMConversion(0, 0, SystemZ::IPM_CC + 1);
uint64_t TopBit = uint64_t(1) << 31;
if (CCMask == (CCValid & SystemZ::CCMASK_0))
return IPMConversion(0, -(1 << SystemZ::IPM_CC), 31);
if (CCMask == (CCValid & (SystemZ::CCMASK_0 | SystemZ::CCMASK_1)))
return IPMConversion(0, -(2 << SystemZ::IPM_CC), 31);
if (CCMask == (CCValid & (SystemZ::CCMASK_0
| SystemZ::CCMASK_1
| SystemZ::CCMASK_2)))
return IPMConversion(0, -(3 << SystemZ::IPM_CC), 31);
if (CCMask == (CCValid & SystemZ::CCMASK_3))
return IPMConversion(0, TopBit - (3 << SystemZ::IPM_CC), 31);
if (CCMask == (CCValid & (SystemZ::CCMASK_1
| SystemZ::CCMASK_2
| SystemZ::CCMASK_3)))
return IPMConversion(0, TopBit - (1 << SystemZ::IPM_CC), 31);
if (CCMask == (CCValid & (SystemZ::CCMASK_0 | SystemZ::CCMASK_2)))
return IPMConversion(-1, 0, SystemZ::IPM_CC);
if (CCMask == (CCValid & (SystemZ::CCMASK_1 | SystemZ::CCMASK_2)))
return IPMConversion(0, 1 << SystemZ::IPM_CC, SystemZ::IPM_CC + 1);
if (CCMask == (CCValid & (SystemZ::CCMASK_0 | SystemZ::CCMASK_3)))
return IPMConversion(0, -(1 << SystemZ::IPM_CC), SystemZ::IPM_CC + 1);
if (CCMask == (CCValid & SystemZ::CCMASK_1))
return IPMConversion(1 << SystemZ::IPM_CC, -(1 << SystemZ::IPM_CC), 31);
if (CCMask == (CCValid & SystemZ::CCMASK_2))
return IPMConversion(1 << SystemZ::IPM_CC,
TopBit - (3 << SystemZ::IPM_CC), 31);
if (CCMask == (CCValid & (SystemZ::CCMASK_0
| SystemZ::CCMASK_1
| SystemZ::CCMASK_3)))
return IPMConversion(1 << SystemZ::IPM_CC, -(3 << SystemZ::IPM_CC), 31);
if (CCMask == (CCValid & (SystemZ::CCMASK_0
| SystemZ::CCMASK_2
| SystemZ::CCMASK_3)))
return IPMConversion(1 << SystemZ::IPM_CC,
TopBit - (1 << SystemZ::IPM_CC), 31);
llvm_unreachable("Unexpected CC combination");
}
SDValue SystemZDAGToDAGISel::expandSelectBoolean(SDNode *Node) {
auto *TrueOp = dyn_cast<ConstantSDNode>(Node->getOperand(0));
auto *FalseOp = dyn_cast<ConstantSDNode>(Node->getOperand(1));
if (!TrueOp || !FalseOp)
return SDValue();
if (FalseOp->getZExtValue() != 0)
return SDValue();
if (TrueOp->getSExtValue() != 1 && TrueOp->getSExtValue() != -1)
return SDValue();
auto *CCValidOp = dyn_cast<ConstantSDNode>(Node->getOperand(2));
auto *CCMaskOp = dyn_cast<ConstantSDNode>(Node->getOperand(3));
if (!CCValidOp || !CCMaskOp)
return SDValue();
int CCValid = CCValidOp->getZExtValue();
int CCMask = CCMaskOp->getZExtValue();
SDLoc DL(Node);
SDValue CCReg = Node->getOperand(4);
IPMConversion IPM = getIPMConversion(CCValid, CCMask);
SDValue Result = CurDAG->getNode(SystemZISD::IPM, DL, MVT::i32, CCReg);
if (IPM.XORValue)
Result = CurDAG->getNode(ISD::XOR, DL, MVT::i32, Result,
CurDAG->getConstant(IPM.XORValue, DL, MVT::i32));
if (IPM.AddValue)
Result = CurDAG->getNode(ISD::ADD, DL, MVT::i32, Result,
CurDAG->getConstant(IPM.AddValue, DL, MVT::i32));
EVT VT = Node->getValueType(0);
if (VT == MVT::i32 && IPM.Bit == 31) {
unsigned ShiftOp = TrueOp->getSExtValue() == 1 ? ISD::SRL : ISD::SRA;
Result = CurDAG->getNode(ShiftOp, DL, MVT::i32, Result,
CurDAG->getConstant(IPM.Bit, DL, MVT::i32));
} else {
if (VT != MVT::i32)
Result = CurDAG->getNode(ISD::ANY_EXTEND, DL, VT, Result);
if (TrueOp->getSExtValue() == 1) {
Result = CurDAG->getNode(ISD::SRL, DL, VT, Result,
CurDAG->getConstant(IPM.Bit, DL, MVT::i32));
Result = CurDAG->getNode(ISD::AND, DL, VT, Result,
CurDAG->getConstant(1, DL, VT));
} else {
int ShlAmt = VT.getSizeInBits() - 1 - IPM.Bit;
int SraAmt = VT.getSizeInBits() - 1;
Result = CurDAG->getNode(ISD::SHL, DL, VT, Result,
CurDAG->getConstant(ShlAmt, DL, MVT::i32));
Result = CurDAG->getNode(ISD::SRA, DL, VT, Result,
CurDAG->getConstant(SraAmt, DL, MVT::i32));
}
}
return Result;
}
void SystemZDAGToDAGISel::PreprocessISelDAG() {
if (Subtarget->hasLoadStoreOnCond2())
return;
bool MadeChange = false;
for (SelectionDAG::allnodes_iterator I = CurDAG->allnodes_begin(),
E = CurDAG->allnodes_end();
I != E;) {
SDNode *N = &*I++;
if (N->use_empty())
continue;
SDValue Res;
switch (N->getOpcode()) {
default: break;
case SystemZISD::SELECT_CCMASK:
Res = expandSelectBoolean(N);
break;
}
if (Res) {
LLVM_DEBUG(dbgs() << "SystemZ DAG preprocessing replacing:\nOld: ");
LLVM_DEBUG(N->dump(CurDAG));
LLVM_DEBUG(dbgs() << "\nNew: ");
LLVM_DEBUG(Res.getNode()->dump(CurDAG));
LLVM_DEBUG(dbgs() << "\n");
CurDAG->ReplaceAllUsesOfValueWith(SDValue(N, 0), Res);
MadeChange = true;
}
}
if (MadeChange)
CurDAG->RemoveDeadNodes();
}