| 1 | //===-- R600ISelDAGToDAG.cpp - A dag to dag inst selector for R600 --------===// |
| 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 | /// \file |
| 10 | /// Defines an instruction selector for the R600 subtarget. |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "AMDGPU.h" |
| 15 | #include "AMDGPUISelDAGToDAG.h" |
| 16 | #include "MCTargetDesc/R600MCTargetDesc.h" |
| 17 | #include "R600.h" |
| 18 | #include "R600Subtarget.h" |
| 19 | |
| 20 | using namespace llvm; |
| 21 | |
| 22 | namespace { |
| 23 | class R600DAGToDAGISel : public AMDGPUDAGToDAGISel { |
| 24 | const R600Subtarget *Subtarget = nullptr; |
| 25 | |
| 26 | bool isConstantLoad(const MemSDNode *N, int cbID) const; |
| 27 | bool SelectGlobalValueConstantOffset(SDValue Addr, SDValue &IntPtr); |
| 28 | bool SelectGlobalValueVariableOffset(SDValue Addr, SDValue &BaseReg, |
| 29 | SDValue &Offset); |
| 30 | |
| 31 | public: |
| 32 | R600DAGToDAGISel() = delete; |
| 33 | |
| 34 | explicit R600DAGToDAGISel(TargetMachine &TM, CodeGenOptLevel OptLevel) |
| 35 | : AMDGPUDAGToDAGISel(TM, OptLevel) {} |
| 36 | |
| 37 | void Select(SDNode *N) override; |
| 38 | |
| 39 | bool SelectADDRIndirect(SDValue Addr, SDValue &Base, |
| 40 | SDValue &Offset) override; |
| 41 | bool SelectADDRVTX_READ(SDValue Addr, SDValue &Base, |
| 42 | SDValue &Offset) override; |
| 43 | |
| 44 | bool runOnMachineFunction(MachineFunction &MF) override; |
| 45 | |
| 46 | void PreprocessISelDAG() override {} |
| 47 | |
| 48 | protected: |
| 49 | // Include the pieces autogenerated from the target description. |
| 50 | #include "R600GenDAGISel.inc" |
| 51 | }; |
| 52 | |
| 53 | class R600DAGToDAGISelLegacy : public SelectionDAGISelLegacy { |
| 54 | public: |
| 55 | static char ID; |
| 56 | explicit R600DAGToDAGISelLegacy(TargetMachine &TM, CodeGenOptLevel OptLevel) |
| 57 | : SelectionDAGISelLegacy( |
| 58 | ID, std::make_unique<R600DAGToDAGISel>(args&: TM, args&: OptLevel)) {} |
| 59 | }; |
| 60 | |
| 61 | char R600DAGToDAGISelLegacy::ID = 0; |
| 62 | |
| 63 | } // namespace |
| 64 | |
| 65 | bool R600DAGToDAGISel::runOnMachineFunction(MachineFunction &MF) { |
| 66 | Subtarget = &MF.getSubtarget<R600Subtarget>(); |
| 67 | return SelectionDAGISel::runOnMachineFunction(mf&: MF); |
| 68 | } |
| 69 | |
| 70 | bool R600DAGToDAGISel::isConstantLoad(const MemSDNode *N, int CbId) const { |
| 71 | if (!N->readMem()) |
| 72 | return false; |
| 73 | if (CbId == -1) |
| 74 | return N->getAddressSpace() == AMDGPUAS::CONSTANT_ADDRESS || |
| 75 | N->getAddressSpace() == AMDGPUAS::CONSTANT_ADDRESS_32BIT; |
| 76 | |
| 77 | return N->getAddressSpace() == AMDGPUAS::CONSTANT_BUFFER_0 + CbId; |
| 78 | } |
| 79 | |
| 80 | bool R600DAGToDAGISel::SelectGlobalValueConstantOffset(SDValue Addr, |
| 81 | SDValue &IntPtr) { |
| 82 | if (ConstantSDNode *Cst = dyn_cast<ConstantSDNode>(Val&: Addr)) { |
| 83 | IntPtr = |
| 84 | CurDAG->getIntPtrConstant(Val: Cst->getZExtValue() / 4, DL: SDLoc(Addr), isTarget: true); |
| 85 | return true; |
| 86 | } |
| 87 | return false; |
| 88 | } |
| 89 | |
| 90 | bool R600DAGToDAGISel::SelectGlobalValueVariableOffset(SDValue Addr, |
| 91 | SDValue &BaseReg, |
| 92 | SDValue &Offset) { |
| 93 | if (!isa<ConstantSDNode>(Val: Addr)) { |
| 94 | BaseReg = Addr; |
| 95 | Offset = CurDAG->getIntPtrConstant(Val: 0, DL: SDLoc(Addr), isTarget: true); |
| 96 | return true; |
| 97 | } |
| 98 | return false; |
| 99 | } |
| 100 | |
| 101 | void R600DAGToDAGISel::Select(SDNode *N) { |
| 102 | unsigned int Opc = N->getOpcode(); |
| 103 | if (N->isMachineOpcode()) { |
| 104 | N->setNodeId(-1); |
| 105 | return; // Already selected. |
| 106 | } |
| 107 | |
| 108 | switch (Opc) { |
| 109 | default: |
| 110 | break; |
| 111 | case AMDGPUISD::BUILD_VERTICAL_VECTOR: |
| 112 | case ISD::SCALAR_TO_VECTOR: |
| 113 | case ISD::BUILD_VECTOR: { |
| 114 | EVT VT = N->getValueType(ResNo: 0); |
| 115 | unsigned NumVectorElts = VT.getVectorNumElements(); |
| 116 | unsigned RegClassID; |
| 117 | // BUILD_VECTOR was lowered into an IMPLICIT_DEF + 4 INSERT_SUBREG |
| 118 | // that adds a 128 bits reg copy when going through TwoAddressInstructions |
| 119 | // pass. We want to avoid 128 bits copies as much as possible because they |
| 120 | // can't be bundled by our scheduler. |
| 121 | switch (NumVectorElts) { |
| 122 | case 2: |
| 123 | RegClassID = R600::R600_Reg64RegClassID; |
| 124 | break; |
| 125 | case 4: |
| 126 | if (Opc == AMDGPUISD::BUILD_VERTICAL_VECTOR) |
| 127 | RegClassID = R600::R600_Reg128VerticalRegClassID; |
| 128 | else |
| 129 | RegClassID = R600::R600_Reg128RegClassID; |
| 130 | break; |
| 131 | default: |
| 132 | llvm_unreachable("Do not know how to lower this BUILD_VECTOR" ); |
| 133 | } |
| 134 | SelectBuildVector(N, RegClassID); |
| 135 | return; |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | SelectCode(N); |
| 140 | } |
| 141 | |
| 142 | bool R600DAGToDAGISel::SelectADDRIndirect(SDValue Addr, SDValue &Base, |
| 143 | SDValue &Offset) { |
| 144 | ConstantSDNode *C; |
| 145 | SDLoc DL(Addr); |
| 146 | |
| 147 | if ((C = dyn_cast<ConstantSDNode>(Val&: Addr))) { |
| 148 | Base = CurDAG->getRegister(Reg: R600::INDIRECT_BASE_ADDR, VT: MVT::i32); |
| 149 | Offset = CurDAG->getTargetConstant(Val: C->getZExtValue(), DL, VT: MVT::i32); |
| 150 | } else if ((Addr.getOpcode() == AMDGPUISD::DWORDADDR) && |
| 151 | (C = dyn_cast<ConstantSDNode>(Val: Addr.getOperand(i: 0)))) { |
| 152 | Base = CurDAG->getRegister(Reg: R600::INDIRECT_BASE_ADDR, VT: MVT::i32); |
| 153 | Offset = CurDAG->getTargetConstant(Val: C->getZExtValue(), DL, VT: MVT::i32); |
| 154 | } else if ((Addr.getOpcode() == ISD::ADD || Addr.getOpcode() == ISD::OR) && |
| 155 | (C = dyn_cast<ConstantSDNode>(Val: Addr.getOperand(i: 1)))) { |
| 156 | Base = Addr.getOperand(i: 0); |
| 157 | Offset = CurDAG->getTargetConstant(Val: C->getZExtValue(), DL, VT: MVT::i32); |
| 158 | } else { |
| 159 | Base = Addr; |
| 160 | Offset = CurDAG->getTargetConstant(Val: 0, DL, VT: MVT::i32); |
| 161 | } |
| 162 | |
| 163 | return true; |
| 164 | } |
| 165 | |
| 166 | bool R600DAGToDAGISel::SelectADDRVTX_READ(SDValue Addr, SDValue &Base, |
| 167 | SDValue &Offset) { |
| 168 | ConstantSDNode *IMMOffset; |
| 169 | |
| 170 | if (Addr.getOpcode() == ISD::ADD && |
| 171 | (IMMOffset = dyn_cast<ConstantSDNode>(Val: Addr.getOperand(i: 1))) && |
| 172 | isInt<16>(x: IMMOffset->getZExtValue())) { |
| 173 | |
| 174 | Base = Addr.getOperand(i: 0); |
| 175 | Offset = CurDAG->getTargetConstant(Val: IMMOffset->getZExtValue(), DL: SDLoc(Addr), |
| 176 | VT: MVT::i32); |
| 177 | return true; |
| 178 | // If the pointer address is constant, we can move it to the offset field. |
| 179 | } |
| 180 | if ((IMMOffset = dyn_cast<ConstantSDNode>(Val&: Addr)) && |
| 181 | isInt<16>(x: IMMOffset->getZExtValue())) { |
| 182 | Base = CurDAG->getCopyFromReg(Chain: CurDAG->getEntryNode(), |
| 183 | dl: SDLoc(CurDAG->getEntryNode()), Reg: R600::ZERO, |
| 184 | VT: MVT::i32); |
| 185 | Offset = CurDAG->getTargetConstant(Val: IMMOffset->getZExtValue(), DL: SDLoc(Addr), |
| 186 | VT: MVT::i32); |
| 187 | return true; |
| 188 | } |
| 189 | |
| 190 | // Default case, no offset |
| 191 | Base = Addr; |
| 192 | Offset = CurDAG->getTargetConstant(Val: 0, DL: SDLoc(Addr), VT: MVT::i32); |
| 193 | return true; |
| 194 | } |
| 195 | |
| 196 | /// This pass converts a legalized DAG into a R600-specific |
| 197 | // DAG, ready for instruction scheduling. |
| 198 | FunctionPass *llvm::createR600ISelDag(TargetMachine &TM, |
| 199 | CodeGenOptLevel OptLevel) { |
| 200 | return new R600DAGToDAGISelLegacy(TM, OptLevel); |
| 201 | } |
| 202 | |