| //===-- VEISelDAGToDAG.cpp - A dag to dag inst selector for VE ------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file defines an instruction selector for the VE target. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "VE.h" |
| #include "VETargetMachine.h" |
| #include "llvm/CodeGen/MachineRegisterInfo.h" |
| #include "llvm/CodeGen/SelectionDAGISel.h" |
| #include "llvm/IR/Intrinsics.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/raw_ostream.h" |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "ve-isel" |
| #define PASS_NAME "VE DAG->DAG Pattern Instruction Selection" |
| |
| //===--------------------------------------------------------------------===// |
| /// VEDAGToDAGISel - VE specific code to select VE machine |
| /// instructions for SelectionDAG operations. |
| /// |
| namespace { |
| class VEDAGToDAGISel : public SelectionDAGISel { |
| /// Subtarget - Keep a pointer to the VE Subtarget around so that we can |
| /// make the right decision when generating code for different targets. |
| const VESubtarget *Subtarget; |
| |
| public: |
| static char ID; |
| |
| VEDAGToDAGISel() = delete; |
| |
| explicit VEDAGToDAGISel(VETargetMachine &tm) : SelectionDAGISel(ID, tm) {} |
| |
| bool runOnMachineFunction(MachineFunction &MF) override { |
| Subtarget = &MF.getSubtarget<VESubtarget>(); |
| return SelectionDAGISel::runOnMachineFunction(MF); |
| } |
| |
| void Select(SDNode *N) override; |
| |
| // Complex Pattern Selectors. |
| bool selectADDRrri(SDValue N, SDValue &Base, SDValue &Index, SDValue &Offset); |
| bool selectADDRrii(SDValue N, SDValue &Base, SDValue &Index, SDValue &Offset); |
| bool selectADDRzri(SDValue N, SDValue &Base, SDValue &Index, SDValue &Offset); |
| bool selectADDRzii(SDValue N, SDValue &Base, SDValue &Index, SDValue &Offset); |
| bool selectADDRri(SDValue N, SDValue &Base, SDValue &Offset); |
| bool selectADDRzi(SDValue N, SDValue &Base, SDValue &Offset); |
| |
| /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for |
| /// inline asm expressions. |
| bool SelectInlineAsmMemoryOperand(const SDValue &Op, |
| InlineAsm::ConstraintCode ConstraintID, |
| std::vector<SDValue> &OutOps) override; |
| |
| // Include the pieces autogenerated from the target description. |
| #include "VEGenDAGISel.inc" |
| |
| private: |
| SDNode *getGlobalBaseReg(); |
| |
| bool matchADDRrr(SDValue N, SDValue &Base, SDValue &Index); |
| bool matchADDRri(SDValue N, SDValue &Base, SDValue &Offset); |
| }; |
| } // end anonymous namespace |
| |
| char VEDAGToDAGISel::ID = 0; |
| |
| INITIALIZE_PASS(VEDAGToDAGISel, DEBUG_TYPE, PASS_NAME, false, false) |
| |
| bool VEDAGToDAGISel::selectADDRrri(SDValue Addr, SDValue &Base, SDValue &Index, |
| SDValue &Offset) { |
| if (Addr.getOpcode() == ISD::FrameIndex) |
| return false; |
| if (Addr.getOpcode() == ISD::TargetExternalSymbol || |
| Addr.getOpcode() == ISD::TargetGlobalAddress || |
| Addr.getOpcode() == ISD::TargetGlobalTLSAddress) |
| return false; // direct calls. |
| |
| SDValue LHS, RHS; |
| if (matchADDRri(Addr, LHS, RHS)) { |
| if (matchADDRrr(LHS, Base, Index)) { |
| Offset = RHS; |
| return true; |
| } |
| // Return false to try selectADDRrii. |
| return false; |
| } |
| if (matchADDRrr(Addr, LHS, RHS)) { |
| // If the input is a pair of a frame-index and a register, move a |
| // frame-index to LHS. This generates MI with following operands. |
| // %dest, #FI, %reg, offset |
| // In the eliminateFrameIndex, above MI is converted to the following. |
| // %dest, %fp, %reg, fi_offset + offset |
| if (isa<FrameIndexSDNode>(RHS)) |
| std::swap(LHS, RHS); |
| |
| if (matchADDRri(RHS, Index, Offset)) { |
| Base = LHS; |
| return true; |
| } |
| if (matchADDRri(LHS, Base, Offset)) { |
| Index = RHS; |
| return true; |
| } |
| Base = LHS; |
| Index = RHS; |
| Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32); |
| return true; |
| } |
| return false; // Let the reg+imm(=0) pattern catch this! |
| } |
| |
| bool VEDAGToDAGISel::selectADDRrii(SDValue Addr, SDValue &Base, SDValue &Index, |
| SDValue &Offset) { |
| if (matchADDRri(Addr, Base, Offset)) { |
| Index = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32); |
| return true; |
| } |
| |
| Base = Addr; |
| Index = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32); |
| Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32); |
| return true; |
| } |
| |
| bool VEDAGToDAGISel::selectADDRzri(SDValue Addr, SDValue &Base, SDValue &Index, |
| SDValue &Offset) { |
| // Prefer ADDRrii. |
| return false; |
| } |
| |
| bool VEDAGToDAGISel::selectADDRzii(SDValue Addr, SDValue &Base, SDValue &Index, |
| SDValue &Offset) { |
| if (isa<FrameIndexSDNode>(Addr)) |
| return false; |
| if (Addr.getOpcode() == ISD::TargetExternalSymbol || |
| Addr.getOpcode() == ISD::TargetGlobalAddress || |
| Addr.getOpcode() == ISD::TargetGlobalTLSAddress) |
| return false; // direct calls. |
| |
| if (auto *CN = dyn_cast<ConstantSDNode>(Addr)) { |
| if (isInt<32>(CN->getSExtValue())) { |
| Base = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32); |
| Index = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32); |
| Offset = |
| CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Addr), MVT::i32); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool VEDAGToDAGISel::selectADDRri(SDValue Addr, SDValue &Base, |
| SDValue &Offset) { |
| if (matchADDRri(Addr, Base, Offset)) |
| return true; |
| |
| Base = Addr; |
| Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32); |
| return true; |
| } |
| |
| bool VEDAGToDAGISel::selectADDRzi(SDValue Addr, SDValue &Base, |
| SDValue &Offset) { |
| if (isa<FrameIndexSDNode>(Addr)) |
| return false; |
| if (Addr.getOpcode() == ISD::TargetExternalSymbol || |
| Addr.getOpcode() == ISD::TargetGlobalAddress || |
| Addr.getOpcode() == ISD::TargetGlobalTLSAddress) |
| return false; // direct calls. |
| |
| if (auto *CN = dyn_cast<ConstantSDNode>(Addr)) { |
| if (isInt<32>(CN->getSExtValue())) { |
| Base = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32); |
| Offset = |
| CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Addr), MVT::i32); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool VEDAGToDAGISel::matchADDRrr(SDValue Addr, SDValue &Base, SDValue &Index) { |
| if (isa<FrameIndexSDNode>(Addr)) |
| return false; |
| if (Addr.getOpcode() == ISD::TargetExternalSymbol || |
| Addr.getOpcode() == ISD::TargetGlobalAddress || |
| Addr.getOpcode() == ISD::TargetGlobalTLSAddress) |
| return false; // direct calls. |
| |
| if (Addr.getOpcode() == ISD::ADD) { |
| ; // Nothing to do here. |
| } else if (Addr.getOpcode() == ISD::OR) { |
| // We want to look through a transform in InstCombine and DAGCombiner that |
| // turns 'add' into 'or', so we can treat this 'or' exactly like an 'add'. |
| if (!CurDAG->haveNoCommonBitsSet(Addr.getOperand(0), Addr.getOperand(1))) |
| return false; |
| } else { |
| return false; |
| } |
| |
| if (Addr.getOperand(0).getOpcode() == VEISD::Lo || |
| Addr.getOperand(1).getOpcode() == VEISD::Lo) |
| return false; // Let the LEASL patterns catch this! |
| |
| Base = Addr.getOperand(0); |
| Index = Addr.getOperand(1); |
| return true; |
| } |
| |
| bool VEDAGToDAGISel::matchADDRri(SDValue Addr, SDValue &Base, SDValue &Offset) { |
| auto AddrTy = Addr->getValueType(0); |
| if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(Addr)) { |
| Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), AddrTy); |
| Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32); |
| return true; |
| } |
| if (Addr.getOpcode() == ISD::TargetExternalSymbol || |
| Addr.getOpcode() == ISD::TargetGlobalAddress || |
| Addr.getOpcode() == ISD::TargetGlobalTLSAddress) |
| return false; // direct calls. |
| |
| if (CurDAG->isBaseWithConstantOffset(Addr)) { |
| ConstantSDNode *CN = cast<ConstantSDNode>(Addr.getOperand(1)); |
| if (isInt<32>(CN->getSExtValue())) { |
| if (FrameIndexSDNode *FIN = |
| dyn_cast<FrameIndexSDNode>(Addr.getOperand(0))) { |
| // Constant offset from frame ref. |
| Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), AddrTy); |
| } else { |
| Base = Addr.getOperand(0); |
| } |
| Offset = |
| CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Addr), MVT::i32); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void VEDAGToDAGISel::Select(SDNode *N) { |
| SDLoc dl(N); |
| if (N->isMachineOpcode()) { |
| N->setNodeId(-1); |
| return; // Already selected. |
| } |
| |
| switch (N->getOpcode()) { |
| |
| // Late eliminate the LEGALAVL wrapper |
| case VEISD::LEGALAVL: |
| ReplaceNode(N, N->getOperand(0).getNode()); |
| return; |
| |
| // Lower (broadcast 1) and (broadcast 0) to VM[P]0 |
| case VEISD::VEC_BROADCAST: { |
| MVT SplatResTy = N->getSimpleValueType(0); |
| if (SplatResTy.getVectorElementType() != MVT::i1) |
| break; |
| |
| // Constant non-zero broadcast. |
| auto BConst = dyn_cast<ConstantSDNode>(N->getOperand(0)); |
| if (!BConst) |
| break; |
| bool BCTrueMask = (BConst->getSExtValue() != 0); |
| if (!BCTrueMask) |
| break; |
| |
| // Packed or non-packed. |
| SDValue New; |
| if (SplatResTy.getVectorNumElements() == StandardVectorWidth) { |
| New = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), SDLoc(N), VE::VM0, |
| MVT::v256i1); |
| } else if (SplatResTy.getVectorNumElements() == PackedVectorWidth) { |
| New = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), SDLoc(N), VE::VMP0, |
| MVT::v512i1); |
| } else |
| break; |
| |
| // Replace. |
| ReplaceNode(N, New.getNode()); |
| return; |
| } |
| |
| case VEISD::GLOBAL_BASE_REG: |
| ReplaceNode(N, getGlobalBaseReg()); |
| return; |
| } |
| |
| SelectCode(N); |
| } |
| |
| /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for |
| /// inline asm expressions. |
| bool VEDAGToDAGISel::SelectInlineAsmMemoryOperand( |
| const SDValue &Op, InlineAsm::ConstraintCode ConstraintID, |
| std::vector<SDValue> &OutOps) { |
| SDValue Op0, Op1; |
| switch (ConstraintID) { |
| default: |
| llvm_unreachable("Unexpected asm memory constraint"); |
| case InlineAsm::ConstraintCode::o: |
| case InlineAsm::ConstraintCode::m: // memory |
| // Try to match ADDRri since reg+imm style is safe for all VE instructions |
| // with a memory operand. |
| if (selectADDRri(Op, Op0, Op1)) { |
| OutOps.push_back(Op0); |
| OutOps.push_back(Op1); |
| return false; |
| } |
| // Otherwise, require the address to be in a register and immediate 0. |
| OutOps.push_back(Op); |
| OutOps.push_back(CurDAG->getTargetConstant(0, SDLoc(Op), MVT::i32)); |
| return false; |
| } |
| return true; |
| } |
| |
| SDNode *VEDAGToDAGISel::getGlobalBaseReg() { |
| Register GlobalBaseReg = Subtarget->getInstrInfo()->getGlobalBaseReg(MF); |
| return CurDAG |
| ->getRegister(GlobalBaseReg, TLI->getPointerTy(CurDAG->getDataLayout())) |
| .getNode(); |
| } |
| |
| /// createVEISelDag - This pass converts a legalized DAG into a |
| /// VE-specific DAG, ready for instruction scheduling. |
| /// |
| FunctionPass *llvm::createVEISelDag(VETargetMachine &TM) { |
| return new VEDAGToDAGISel(TM); |
| } |