//===-- LanaiISelDAGToDAG.cpp - A dag to dag inst selector for Lanai ------===// // // 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 Lanai target. // //===----------------------------------------------------------------------===// #include "LanaiAluCode.h" #include "LanaiMachineFunctionInfo.h" #include "LanaiRegisterInfo.h" #include "LanaiSubtarget.h" #include "LanaiTargetMachine.h" #include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/SelectionDAGISel.h" #include "llvm/IR/CFG.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Type.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" using namespace llvm; #define DEBUG_TYPE "lanai-isel" #define PASS_NAME "Lanai DAG->DAG Pattern Instruction Selection" //===----------------------------------------------------------------------===// // Instruction Selector Implementation //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // LanaiDAGToDAGISel - Lanai specific code to select Lanai machine // instructions for SelectionDAG operations. //===----------------------------------------------------------------------===// namespace { class LanaiDAGToDAGISel : public SelectionDAGISel { public: LanaiDAGToDAGISel() = delete; explicit LanaiDAGToDAGISel(LanaiTargetMachine &TargetMachine) : SelectionDAGISel(TargetMachine) {} bool SelectInlineAsmMemoryOperand(const SDValue &Op, InlineAsm::ConstraintCode ConstraintCode, std::vector &OutOps) override; private: // Include the pieces autogenerated from the target description. #include "LanaiGenDAGISel.inc" // Instruction Selection not handled by the auto-generated tablgen void Select(SDNode *N) override; // Support functions for the opcodes of Instruction Selection // not handled by the auto-generated tablgen void selectFrameIndex(SDNode *N); // Complex Pattern for address selection. bool selectAddrRi(SDValue Addr, SDValue &Base, SDValue &Offset, SDValue &AluOp); bool selectAddrRr(SDValue Addr, SDValue &R1, SDValue &R2, SDValue &AluOp); bool selectAddrSls(SDValue Addr, SDValue &Offset); bool selectAddrSpls(SDValue Addr, SDValue &Base, SDValue &Offset, SDValue &AluOp); // getI32Imm - Return a target constant with the specified value, of type i32. inline SDValue getI32Imm(unsigned Imm, const SDLoc &DL) { return CurDAG->getTargetConstant(Imm, DL, MVT::i32); } private: bool selectAddrRiSpls(SDValue Addr, SDValue &Base, SDValue &Offset, SDValue &AluOp, bool RiMode); }; bool canBeRepresentedAsSls(const ConstantSDNode &CN) { // Fits in 21-bit signed immediate and two low-order bits are zero. return isInt<21>(CN.getSExtValue()) && ((CN.getSExtValue() & 0x3) == 0); } class LanaiDAGToDAGISelLegacy : public SelectionDAGISelLegacy { public: static char ID; explicit LanaiDAGToDAGISelLegacy(LanaiTargetMachine &TM) : SelectionDAGISelLegacy(ID, std::make_unique(TM)) {} }; } // namespace char LanaiDAGToDAGISelLegacy::ID = 0; INITIALIZE_PASS(LanaiDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false) // Helper functions for ComplexPattern used on LanaiInstrInfo // Used on Lanai Load/Store instructions. bool LanaiDAGToDAGISel::selectAddrSls(SDValue Addr, SDValue &Offset) { if (ConstantSDNode *CN = dyn_cast(Addr)) { SDLoc DL(Addr); // Loading from a constant address. if (canBeRepresentedAsSls(*CN)) { int32_t Imm = CN->getSExtValue(); Offset = CurDAG->getTargetConstant(Imm, DL, CN->getValueType(0)); return true; } } if (Addr.getOpcode() == ISD::OR && Addr.getOperand(1).getOpcode() == LanaiISD::SMALL) { Offset = Addr.getOperand(1).getOperand(0); return true; } return false; } bool LanaiDAGToDAGISel::selectAddrRiSpls(SDValue Addr, SDValue &Base, SDValue &Offset, SDValue &AluOp, bool RiMode) { SDLoc DL(Addr); if (ConstantSDNode *CN = dyn_cast(Addr)) { if (RiMode) { // Fits in 16-bit signed immediate. if (isInt<16>(CN->getSExtValue())) { int16_t Imm = CN->getSExtValue(); Offset = CurDAG->getTargetConstant(Imm, DL, CN->getValueType(0)); Base = CurDAG->getRegister(Lanai::R0, CN->getValueType(0)); AluOp = CurDAG->getTargetConstant(LPAC::ADD, DL, MVT::i32); return true; } // Allow SLS to match if the constant doesn't fit in 16 bits but can be // represented as an SLS. if (canBeRepresentedAsSls(*CN)) return false; } else { // Fits in 10-bit signed immediate. if (isInt<10>(CN->getSExtValue())) { int16_t Imm = CN->getSExtValue(); Offset = CurDAG->getTargetConstant(Imm, DL, CN->getValueType(0)); Base = CurDAG->getRegister(Lanai::R0, CN->getValueType(0)); AluOp = CurDAG->getTargetConstant(LPAC::ADD, DL, MVT::i32); return true; } } } // if Address is FI, get the TargetFrameIndex. if (FrameIndexSDNode *FIN = dyn_cast(Addr)) { Base = CurDAG->getTargetFrameIndex( FIN->getIndex(), getTargetLowering()->getPointerTy(CurDAG->getDataLayout())); Offset = CurDAG->getTargetConstant(0, DL, MVT::i32); AluOp = CurDAG->getTargetConstant(LPAC::ADD, DL, MVT::i32); return true; } // Skip direct calls if ((Addr.getOpcode() == ISD::TargetExternalSymbol || Addr.getOpcode() == ISD::TargetGlobalAddress)) return false; // Address of the form imm + reg ISD::NodeType AluOperator = static_cast(Addr.getOpcode()); if (AluOperator == ISD::ADD) { AluOp = CurDAG->getTargetConstant(LPAC::ADD, DL, MVT::i32); // Addresses of the form FI+const if (ConstantSDNode *CN = dyn_cast(Addr.getOperand(1))) if ((RiMode && isInt<16>(CN->getSExtValue())) || (!RiMode && isInt<10>(CN->getSExtValue()))) { // If the first operand is a FI, get the TargetFI Node if (FrameIndexSDNode *FIN = dyn_cast(Addr.getOperand(0))) { Base = CurDAG->getTargetFrameIndex( FIN->getIndex(), getTargetLowering()->getPointerTy(CurDAG->getDataLayout())); } else { Base = Addr.getOperand(0); } Offset = CurDAG->getTargetConstant(CN->getSExtValue(), DL, MVT::i32); return true; } } // Let SLS match SMALL instead of RI. if (AluOperator == ISD::OR && RiMode && Addr.getOperand(1).getOpcode() == LanaiISD::SMALL) return false; Base = Addr; Offset = CurDAG->getTargetConstant(0, DL, MVT::i32); AluOp = CurDAG->getTargetConstant(LPAC::ADD, DL, MVT::i32); return true; } bool LanaiDAGToDAGISel::selectAddrRi(SDValue Addr, SDValue &Base, SDValue &Offset, SDValue &AluOp) { return selectAddrRiSpls(Addr, Base, Offset, AluOp, /*RiMode=*/true); } bool LanaiDAGToDAGISel::selectAddrSpls(SDValue Addr, SDValue &Base, SDValue &Offset, SDValue &AluOp) { return selectAddrRiSpls(Addr, Base, Offset, AluOp, /*RiMode=*/false); } namespace llvm { namespace LPAC { static AluCode isdToLanaiAluCode(ISD::NodeType Node_type) { switch (Node_type) { case ISD::ADD: return AluCode::ADD; case ISD::ADDE: return AluCode::ADDC; case ISD::SUB: return AluCode::SUB; case ISD::SUBE: return AluCode::SUBB; case ISD::AND: return AluCode::AND; case ISD::OR: return AluCode::OR; case ISD::XOR: return AluCode::XOR; case ISD::SHL: return AluCode::SHL; case ISD::SRL: return AluCode::SRL; case ISD::SRA: return AluCode::SRA; default: return AluCode::UNKNOWN; } } } // namespace LPAC } // namespace llvm bool LanaiDAGToDAGISel::selectAddrRr(SDValue Addr, SDValue &R1, SDValue &R2, SDValue &AluOp) { // if Address is FI, get the TargetFrameIndex. if (Addr.getOpcode() == ISD::FrameIndex) return false; // Skip direct calls if ((Addr.getOpcode() == ISD::TargetExternalSymbol || Addr.getOpcode() == ISD::TargetGlobalAddress)) return false; // Address of the form OP + OP ISD::NodeType AluOperator = static_cast(Addr.getOpcode()); LPAC::AluCode AluCode = LPAC::isdToLanaiAluCode(AluOperator); if (AluCode != LPAC::UNKNOWN) { // Skip addresses of the form FI OP const if (ConstantSDNode *CN = dyn_cast(Addr.getOperand(1))) if (isInt<16>(CN->getSExtValue())) return false; // Skip addresses with hi/lo operands if (Addr.getOperand(0).getOpcode() == LanaiISD::HI || Addr.getOperand(0).getOpcode() == LanaiISD::LO || Addr.getOperand(0).getOpcode() == LanaiISD::SMALL || Addr.getOperand(1).getOpcode() == LanaiISD::HI || Addr.getOperand(1).getOpcode() == LanaiISD::LO || Addr.getOperand(1).getOpcode() == LanaiISD::SMALL) return false; // Addresses of the form register OP register R1 = Addr.getOperand(0); R2 = Addr.getOperand(1); AluOp = CurDAG->getTargetConstant(AluCode, SDLoc(Addr), MVT::i32); return true; } // Skip addresses with zero offset return false; } bool LanaiDAGToDAGISel::SelectInlineAsmMemoryOperand( const SDValue &Op, InlineAsm::ConstraintCode ConstraintCode, std::vector &OutOps) { SDValue Op0, Op1, AluOp; switch (ConstraintCode) { default: return true; case InlineAsm::ConstraintCode::m: // memory if (!selectAddrRr(Op, Op0, Op1, AluOp) && !selectAddrRi(Op, Op0, Op1, AluOp)) return true; break; } OutOps.push_back(Op0); OutOps.push_back(Op1); OutOps.push_back(AluOp); return false; } // Select instructions not customized! Used for // expanded, promoted and normal instructions void LanaiDAGToDAGISel::Select(SDNode *Node) { unsigned Opcode = Node->getOpcode(); // If we have a custom node, we already have selected! if (Node->isMachineOpcode()) { LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); return; } // Instruction Selection not handled by the auto-generated tablegen selection // should be handled here. EVT VT = Node->getValueType(0); switch (Opcode) { case ISD::Constant: if (VT == MVT::i32) { ConstantSDNode *ConstNode = cast(Node); // Materialize zero constants as copies from R0. This allows the coalescer // to propagate these into other instructions. if (ConstNode->isZero()) { SDValue New = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), SDLoc(Node), Lanai::R0, MVT::i32); return ReplaceNode(Node, New.getNode()); } // Materialize all ones constants as copies from R1. This allows the // coalescer to propagate these into other instructions. if (ConstNode->isAllOnes()) { SDValue New = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), SDLoc(Node), Lanai::R1, MVT::i32); return ReplaceNode(Node, New.getNode()); } } break; case ISD::FrameIndex: selectFrameIndex(Node); return; default: break; } // Select the default instruction SelectCode(Node); } void LanaiDAGToDAGISel::selectFrameIndex(SDNode *Node) { SDLoc DL(Node); SDValue Imm = CurDAG->getTargetConstant(0, DL, MVT::i32); int FI = cast(Node)->getIndex(); EVT VT = Node->getValueType(0); SDValue TFI = CurDAG->getTargetFrameIndex(FI, VT); unsigned Opc = Lanai::ADD_I_LO; if (Node->hasOneUse()) { CurDAG->SelectNodeTo(Node, Opc, VT, TFI, Imm); return; } ReplaceNode(Node, CurDAG->getMachineNode(Opc, DL, VT, TFI, Imm)); } // createLanaiISelDag - This pass converts a legalized DAG into a // Lanai-specific DAG, ready for instruction scheduling. FunctionPass *llvm::createLanaiISelDag(LanaiTargetMachine &TM) { return new LanaiDAGToDAGISelLegacy(TM); }