//===-- 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: VEDAGToDAGISel() = delete; explicit VEDAGToDAGISel(VETargetMachine &tm) : SelectionDAGISel(tm) {} bool runOnMachineFunction(MachineFunction &MF) override { Subtarget = &MF.getSubtarget(); 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 &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); }; class VEDAGToDAGISelLegacy : public SelectionDAGISelLegacy { public: static char ID; explicit VEDAGToDAGISelLegacy(VETargetMachine &tm) : SelectionDAGISelLegacy(ID, std::make_unique(tm)) {} }; } // end anonymous namespace char VEDAGToDAGISelLegacy::ID = 0; INITIALIZE_PASS(VEDAGToDAGISelLegacy, 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(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(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(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(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(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(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(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(Addr.getOperand(1)); if (isInt<32>(CN->getSExtValue())) { if (FrameIndexSDNode *FIN = dyn_cast(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(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 &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 VEDAGToDAGISelLegacy(TM); }