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