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