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