| 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 | |