1//===-- XCoreISelDAGToDAG.cpp - A dag to dag inst selector for XCore ------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines an instruction selector for the XCore target.
10//
11//===----------------------------------------------------------------------===//
12
13#include "XCore.h"
14#include "XCoreTargetMachine.h"
15#include "llvm/CodeGen/MachineFrameInfo.h"
16#include "llvm/CodeGen/MachineFunction.h"
17#include "llvm/CodeGen/MachineInstrBuilder.h"
18#include "llvm/CodeGen/MachineRegisterInfo.h"
19#include "llvm/CodeGen/SelectionDAG.h"
20#include "llvm/CodeGen/SelectionDAGISel.h"
21#include "llvm/CodeGen/TargetLowering.h"
22#include "llvm/IR/CallingConv.h"
23#include "llvm/IR/Constants.h"
24#include "llvm/IR/DerivedTypes.h"
25#include "llvm/IR/Function.h"
26#include "llvm/IR/Intrinsics.h"
27#include "llvm/IR/IntrinsicsXCore.h"
28#include "llvm/IR/LLVMContext.h"
29#include "llvm/Support/Debug.h"
30#include "llvm/Support/ErrorHandling.h"
31#include "llvm/Support/raw_ostream.h"
32using namespace llvm;
33
34/// XCoreDAGToDAGISel - XCore specific code to select XCore machine
35/// instructions for SelectionDAG operations.
36///
37namespace {
38  class XCoreDAGToDAGISel : public SelectionDAGISel {
39
40  public:
41    XCoreDAGToDAGISel(XCoreTargetMachine &TM, CodeGenOpt::Level OptLevel)
42      : SelectionDAGISel(TM, OptLevel) {}
43
44    void Select(SDNode *N) override;
45    bool tryBRIND(SDNode *N);
46
47    /// getI32Imm - Return a target constant with the specified value, of type
48    /// i32.
49    inline SDValue getI32Imm(unsigned Imm, const SDLoc &dl) {
50      return CurDAG->getTargetConstant(Imm, dl, MVT::i32);
51    }
52
53    inline bool immMskBitp(SDNode *inN) const {
54      ConstantSDNode *N = cast<ConstantSDNode>(inN);
55      uint32_t value = (uint32_t)N->getZExtValue();
56      if (!isMask_32(value)) {
57        return false;
58      }
59      int msksize = 32 - countLeadingZeros(value);
60      return (msksize >= 1 && msksize <= 8) ||
61              msksize == 16 || msksize == 24 || msksize == 32;
62    }
63
64    // Complex Pattern Selectors.
65    bool SelectADDRspii(SDValue Addr, SDValue &Base, SDValue &Offset);
66
67    bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
68                                      std::vector<SDValue> &OutOps) override;
69
70    StringRef getPassName() const override {
71      return "XCore DAG->DAG Pattern Instruction Selection";
72    }
73
74    // Include the pieces autogenerated from the target description.
75  #include "XCoreGenDAGISel.inc"
76  };
77}  // end anonymous namespace
78
79/// createXCoreISelDag - This pass converts a legalized DAG into a
80/// XCore-specific DAG, ready for instruction scheduling.
81///
82FunctionPass *llvm::createXCoreISelDag(XCoreTargetMachine &TM,
83                                       CodeGenOpt::Level OptLevel) {
84  return new XCoreDAGToDAGISel(TM, OptLevel);
85}
86
87bool XCoreDAGToDAGISel::SelectADDRspii(SDValue Addr, SDValue &Base,
88                                       SDValue &Offset) {
89  FrameIndexSDNode *FIN = nullptr;
90  if ((FIN = dyn_cast<FrameIndexSDNode>(Addr))) {
91    Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i32);
92    Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);
93    return true;
94  }
95  if (Addr.getOpcode() == ISD::ADD) {
96    ConstantSDNode *CN = nullptr;
97    if ((FIN = dyn_cast<FrameIndexSDNode>(Addr.getOperand(0)))
98      && (CN = dyn_cast<ConstantSDNode>(Addr.getOperand(1)))
99      && (CN->getSExtValue() % 4 == 0 && CN->getSExtValue() >= 0)) {
100      // Constant positive word offset from frame index
101      Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i32);
102      Offset = CurDAG->getTargetConstant(CN->getSExtValue(), SDLoc(Addr),
103                                         MVT::i32);
104      return true;
105    }
106  }
107  return false;
108}
109
110bool XCoreDAGToDAGISel::
111SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
112                             std::vector<SDValue> &OutOps) {
113  SDValue Reg;
114  switch (ConstraintID) {
115  default: return true;
116  case InlineAsm::Constraint_m: // Memory.
117    switch (Op.getOpcode()) {
118    default: return true;
119    case XCoreISD::CPRelativeWrapper:
120      Reg = CurDAG->getRegister(XCore::CP, MVT::i32);
121      break;
122    case XCoreISD::DPRelativeWrapper:
123      Reg = CurDAG->getRegister(XCore::DP, MVT::i32);
124      break;
125    }
126  }
127  OutOps.push_back(Reg);
128  OutOps.push_back(Op.getOperand(0));
129  return false;
130}
131
132void XCoreDAGToDAGISel::Select(SDNode *N) {
133  SDLoc dl(N);
134  switch (N->getOpcode()) {
135  default: break;
136  case ISD::Constant: {
137    uint64_t Val = cast<ConstantSDNode>(N)->getZExtValue();
138    if (immMskBitp(N)) {
139      // Transformation function: get the size of a mask
140      // Look for the first non-zero bit
141      SDValue MskSize = getI32Imm(32 - countLeadingZeros((uint32_t)Val), dl);
142      ReplaceNode(N, CurDAG->getMachineNode(XCore::MKMSK_rus, dl,
143                                            MVT::i32, MskSize));
144      return;
145    }
146    else if (!isUInt<16>(Val)) {
147      SDValue CPIdx = CurDAG->getTargetConstantPool(
148          ConstantInt::get(Type::getInt32Ty(*CurDAG->getContext()), Val),
149          getTargetLowering()->getPointerTy(CurDAG->getDataLayout()));
150      SDNode *node = CurDAG->getMachineNode(XCore::LDWCP_lru6, dl, MVT::i32,
151                                            MVT::Other, CPIdx,
152                                            CurDAG->getEntryNode());
153      MachineMemOperand *MemOp =
154          MF->getMachineMemOperand(MachinePointerInfo::getConstantPool(*MF),
155                                   MachineMemOperand::MOLoad, 4, Align(4));
156      CurDAG->setNodeMemRefs(cast<MachineSDNode>(node), {MemOp});
157      ReplaceNode(N, node);
158      return;
159    }
160    break;
161  }
162  case XCoreISD::LADD: {
163    SDValue Ops[] = { N->getOperand(0), N->getOperand(1),
164                        N->getOperand(2) };
165    ReplaceNode(N, CurDAG->getMachineNode(XCore::LADD_l5r, dl, MVT::i32,
166                                          MVT::i32, Ops));
167    return;
168  }
169  case XCoreISD::LSUB: {
170    SDValue Ops[] = { N->getOperand(0), N->getOperand(1),
171                        N->getOperand(2) };
172    ReplaceNode(N, CurDAG->getMachineNode(XCore::LSUB_l5r, dl, MVT::i32,
173                                          MVT::i32, Ops));
174    return;
175  }
176  case XCoreISD::MACCU: {
177    SDValue Ops[] = { N->getOperand(0), N->getOperand(1),
178                      N->getOperand(2), N->getOperand(3) };
179    ReplaceNode(N, CurDAG->getMachineNode(XCore::MACCU_l4r, dl, MVT::i32,
180                                          MVT::i32, Ops));
181    return;
182  }
183  case XCoreISD::MACCS: {
184    SDValue Ops[] = { N->getOperand(0), N->getOperand(1),
185                      N->getOperand(2), N->getOperand(3) };
186    ReplaceNode(N, CurDAG->getMachineNode(XCore::MACCS_l4r, dl, MVT::i32,
187                                          MVT::i32, Ops));
188    return;
189  }
190  case XCoreISD::LMUL: {
191    SDValue Ops[] = { N->getOperand(0), N->getOperand(1),
192                      N->getOperand(2), N->getOperand(3) };
193    ReplaceNode(N, CurDAG->getMachineNode(XCore::LMUL_l6r, dl, MVT::i32,
194                                          MVT::i32, Ops));
195    return;
196  }
197  case XCoreISD::CRC8: {
198    SDValue Ops[] = { N->getOperand(0), N->getOperand(1), N->getOperand(2) };
199    ReplaceNode(N, CurDAG->getMachineNode(XCore::CRC8_l4r, dl, MVT::i32,
200                                          MVT::i32, Ops));
201    return;
202  }
203  case ISD::BRIND:
204    if (tryBRIND(N))
205      return;
206    break;
207  // Other cases are autogenerated.
208  }
209  SelectCode(N);
210}
211
212/// Given a chain return a new chain where any appearance of Old is replaced
213/// by New. There must be at most one instruction between Old and Chain and
214/// this instruction must be a TokenFactor. Returns an empty SDValue if
215/// these conditions don't hold.
216static SDValue
217replaceInChain(SelectionDAG *CurDAG, SDValue Chain, SDValue Old, SDValue New)
218{
219  if (Chain == Old)
220    return New;
221  if (Chain->getOpcode() != ISD::TokenFactor)
222    return SDValue();
223  SmallVector<SDValue, 8> Ops;
224  bool found = false;
225  for (unsigned i = 0, e = Chain->getNumOperands(); i != e; ++i) {
226    if (Chain->getOperand(i) == Old) {
227      Ops.push_back(New);
228      found = true;
229    } else {
230      Ops.push_back(Chain->getOperand(i));
231    }
232  }
233  if (!found)
234    return SDValue();
235  return CurDAG->getNode(ISD::TokenFactor, SDLoc(Chain), MVT::Other, Ops);
236}
237
238bool XCoreDAGToDAGISel::tryBRIND(SDNode *N) {
239  SDLoc dl(N);
240  // (brind (int_xcore_checkevent (addr)))
241  SDValue Chain = N->getOperand(0);
242  SDValue Addr = N->getOperand(1);
243  if (Addr->getOpcode() != ISD::INTRINSIC_W_CHAIN)
244    return false;
245  unsigned IntNo = cast<ConstantSDNode>(Addr->getOperand(1))->getZExtValue();
246  if (IntNo != Intrinsic::xcore_checkevent)
247    return false;
248  SDValue nextAddr = Addr->getOperand(2);
249  SDValue CheckEventChainOut(Addr.getNode(), 1);
250  if (!CheckEventChainOut.use_empty()) {
251    // If the chain out of the checkevent intrinsic is an operand of the
252    // indirect branch or used in a TokenFactor which is the operand of the
253    // indirect branch then build a new chain which uses the chain coming into
254    // the checkevent intrinsic instead.
255    SDValue CheckEventChainIn = Addr->getOperand(0);
256    SDValue NewChain = replaceInChain(CurDAG, Chain, CheckEventChainOut,
257                                      CheckEventChainIn);
258    if (!NewChain.getNode())
259      return false;
260    Chain = NewChain;
261  }
262  // Enable events on the thread using setsr 1 and then disable them immediately
263  // after with clrsr 1. If any resources owned by the thread are ready an event
264  // will be taken. If no resource is ready we branch to the address which was
265  // the operand to the checkevent intrinsic.
266  SDValue constOne = getI32Imm(1, dl);
267  SDValue Glue =
268    SDValue(CurDAG->getMachineNode(XCore::SETSR_branch_u6, dl, MVT::Glue,
269                                   constOne, Chain), 0);
270  Glue =
271    SDValue(CurDAG->getMachineNode(XCore::CLRSR_branch_u6, dl, MVT::Glue,
272                                   constOne, Glue), 0);
273  if (nextAddr->getOpcode() == XCoreISD::PCRelativeWrapper &&
274      nextAddr->getOperand(0)->getOpcode() == ISD::TargetBlockAddress) {
275    CurDAG->SelectNodeTo(N, XCore::BRFU_lu6, MVT::Other,
276                         nextAddr->getOperand(0), Glue);
277    return true;
278  }
279  CurDAG->SelectNodeTo(N, XCore::BAU_1r, MVT::Other, nextAddr, Glue);
280  return true;
281}
282