| //===-- 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 "Lanai.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" |
| |
| //===----------------------------------------------------------------------===// |
| // Instruction Selector Implementation |
| //===----------------------------------------------------------------------===// |
| |
| //===----------------------------------------------------------------------===// |
| // LanaiDAGToDAGISel - Lanai specific code to select Lanai machine |
| // instructions for SelectionDAG operations. |
| //===----------------------------------------------------------------------===// |
| namespace { |
| |
| class LanaiDAGToDAGISel : public SelectionDAGISel { |
| public: |
| explicit LanaiDAGToDAGISel(LanaiTargetMachine &TargetMachine) |
| : SelectionDAGISel(TargetMachine) {} |
| |
| bool runOnMachineFunction(MachineFunction &MF) override { |
| return SelectionDAGISel::runOnMachineFunction(MF); |
| } |
| |
| // Pass Name |
| StringRef getPassName() const override { |
| return "Lanai DAG->DAG Pattern Instruction Selection"; |
| } |
| |
| bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintCode, |
| std::vector<SDValue> &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); |
| } |
| |
| } // namespace |
| |
| // 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<ConstantSDNode>(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<ConstantSDNode>(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<FrameIndexSDNode>(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<ISD::NodeType>(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<ConstantSDNode>(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<FrameIndexSDNode>(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); |
| } |
| |
| 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<ISD::NodeType>(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<ConstantSDNode>(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, unsigned ConstraintCode, std::vector<SDValue> &OutOps) { |
| SDValue Op0, Op1, AluOp; |
| switch (ConstraintCode) { |
| default: |
| return true; |
| case InlineAsm::Constraint_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<ConstantSDNode>(Node); |
| // Materialize zero constants as copies from R0. This allows the coalescer |
| // to propagate these into other instructions. |
| if (ConstNode->isNullValue()) { |
| 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->isAllOnesValue()) { |
| 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<FrameIndexSDNode>(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 LanaiDAGToDAGISel(TM); |
| } |