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 | 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 | |
90 | char XCoreDAGToDAGISelLegacy::ID = 0; |
91 | |
92 | INITIALIZE_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 | /// |
97 | FunctionPass *llvm::createXCoreISelDag(XCoreTargetMachine &TM, |
98 | CodeGenOptLevel OptLevel) { |
99 | return new XCoreDAGToDAGISelLegacy(TM, OptLevel); |
100 | } |
101 | |
102 | bool 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 | |
125 | bool 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 | |
147 | void 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. |
231 | static SDValue |
232 | replaceInChain(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 | |
253 | bool 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 | |