//===-- SparcISelDAGToDAG.cpp - A dag to dag inst selector for Sparc ------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines an instruction selector for the SPARC target. // //===----------------------------------------------------------------------===// #include "SparcTargetMachine.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/SelectionDAGISel.h" #include "llvm/IR/Intrinsics.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; #define DEBUG_TYPE "sparc-isel" #define PASS_NAME "SPARC DAG->DAG Pattern Instruction Selection" //===----------------------------------------------------------------------===// // Instruction Selector Implementation //===----------------------------------------------------------------------===// //===--------------------------------------------------------------------===// /// SparcDAGToDAGISel - SPARC specific code to select SPARC machine /// instructions for SelectionDAG operations. /// namespace { class SparcDAGToDAGISel : public SelectionDAGISel { /// Subtarget - Keep a pointer to the Sparc Subtarget around so that we can /// make the right decision when generating code for different targets. const SparcSubtarget *Subtarget = nullptr; public: SparcDAGToDAGISel() = delete; explicit SparcDAGToDAGISel(SparcTargetMachine &tm) : SelectionDAGISel(tm) {} bool runOnMachineFunction(MachineFunction &MF) override { Subtarget = &MF.getSubtarget(); return SelectionDAGISel::runOnMachineFunction(MF); } void Select(SDNode *N) override; // Complex Pattern Selectors. bool SelectADDRrr(SDValue N, SDValue &R1, SDValue &R2); bool SelectADDRri(SDValue N, SDValue &Base, SDValue &Offset); /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for /// inline asm expressions. bool SelectInlineAsmMemoryOperand(const SDValue &Op, InlineAsm::ConstraintCode ConstraintID, std::vector &OutOps) override; // Include the pieces autogenerated from the target description. #include "SparcGenDAGISel.inc" private: SDNode* getGlobalBaseReg(); bool tryInlineAsm(SDNode *N); }; class SparcDAGToDAGISelLegacy : public SelectionDAGISelLegacy { public: static char ID; explicit SparcDAGToDAGISelLegacy(SparcTargetMachine &tm) : SelectionDAGISelLegacy(ID, std::make_unique(tm)) {} }; } // end anonymous namespace char SparcDAGToDAGISelLegacy::ID = 0; INITIALIZE_PASS(SparcDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false) SDNode* SparcDAGToDAGISel::getGlobalBaseReg() { Register GlobalBaseReg = Subtarget->getInstrInfo()->getGlobalBaseReg(MF); return CurDAG->getRegister(GlobalBaseReg, TLI->getPointerTy(CurDAG->getDataLayout())) .getNode(); } bool SparcDAGToDAGISel::SelectADDRri(SDValue Addr, SDValue &Base, SDValue &Offset) { if (FrameIndexSDNode *FIN = dyn_cast(Addr)) { Base = CurDAG->getTargetFrameIndex( FIN->getIndex(), TLI->getPointerTy(CurDAG->getDataLayout())); Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32); return true; } if (Addr.getOpcode() == ISD::TargetExternalSymbol || Addr.getOpcode() == ISD::TargetGlobalAddress || Addr.getOpcode() == ISD::TargetGlobalTLSAddress) return false; // direct calls. if (Addr.getOpcode() == ISD::ADD) { if (ConstantSDNode *CN = dyn_cast(Addr.getOperand(1))) { if (isInt<13>(CN->getSExtValue())) { if (FrameIndexSDNode *FIN = dyn_cast(Addr.getOperand(0))) { // Constant offset from frame ref. Base = CurDAG->getTargetFrameIndex( FIN->getIndex(), TLI->getPointerTy(CurDAG->getDataLayout())); } else { Base = Addr.getOperand(0); } Offset = CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Addr), MVT::i32); return true; } } if (Addr.getOperand(0).getOpcode() == SPISD::Lo) { Base = Addr.getOperand(1); Offset = Addr.getOperand(0).getOperand(0); return true; } if (Addr.getOperand(1).getOpcode() == SPISD::Lo) { Base = Addr.getOperand(0); Offset = Addr.getOperand(1).getOperand(0); return true; } } Base = Addr; Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32); return true; } bool SparcDAGToDAGISel::SelectADDRrr(SDValue Addr, SDValue &R1, SDValue &R2) { if (Addr.getOpcode() == ISD::FrameIndex) return false; if (Addr.getOpcode() == ISD::TargetExternalSymbol || Addr.getOpcode() == ISD::TargetGlobalAddress || Addr.getOpcode() == ISD::TargetGlobalTLSAddress) return false; // direct calls. if (Addr.getOpcode() == ISD::ADD) { if (ConstantSDNode *CN = dyn_cast(Addr.getOperand(1))) if (isInt<13>(CN->getSExtValue())) return false; // Let the reg+imm pattern catch this! if (Addr.getOperand(0).getOpcode() == SPISD::Lo || Addr.getOperand(1).getOpcode() == SPISD::Lo) return false; // Let the reg+imm pattern catch this! R1 = Addr.getOperand(0); R2 = Addr.getOperand(1); return true; } R1 = Addr; R2 = CurDAG->getRegister(SP::G0, TLI->getPointerTy(CurDAG->getDataLayout())); return true; } // Re-assemble i64 arguments split up in SelectionDAGBuilder's // visitInlineAsm / GetRegistersForValue functions. // // Note: This function was copied from, and is essentially identical // to ARMISelDAGToDAG::SelectInlineAsm. It is very unfortunate that // such hacking-up is necessary; a rethink of how inline asm operands // are handled may be in order to make doing this more sane. // // TODO: fix inline asm support so I can simply tell it that 'i64' // inputs to asm need to be allocated to the IntPair register type, // and have that work. Then, delete this function. bool SparcDAGToDAGISel::tryInlineAsm(SDNode *N){ std::vector AsmNodeOperands; InlineAsm::Flag Flag; bool Changed = false; unsigned NumOps = N->getNumOperands(); // Normally, i64 data is bounded to two arbitrary GPRs for "%r" // constraint. However, some instructions (e.g. ldd/std) require // (even/even+1) GPRs. // So, here, we check for this case, and mutate the inlineasm to use // a single IntPair register instead, which guarantees such even/odd // placement. SDLoc dl(N); SDValue Glue = N->getGluedNode() ? N->getOperand(NumOps - 1) : SDValue(); SmallVector OpChanged; // Glue node will be appended late. for(unsigned i = 0, e = N->getGluedNode() ? NumOps - 1 : NumOps; i < e; ++i) { SDValue op = N->getOperand(i); AsmNodeOperands.push_back(op); if (i < InlineAsm::Op_FirstOperand) continue; if (const auto *C = dyn_cast(N->getOperand(i))) Flag = InlineAsm::Flag(C->getZExtValue()); else continue; // Immediate operands to inline asm in the SelectionDAG are modeled with // two operands. The first is a constant of value InlineAsm::Kind::Imm, and // the second is a constant with the value of the immediate. If we get here // and we have a Kind::Imm, skip the next operand, and continue. if (Flag.isImmKind()) { SDValue op = N->getOperand(++i); AsmNodeOperands.push_back(op); continue; } const unsigned NumRegs = Flag.getNumOperandRegisters(); if (NumRegs) OpChanged.push_back(false); unsigned DefIdx = 0; bool IsTiedToChangedOp = false; // If it's a use that is tied with a previous def, it has no // reg class constraint. if (Changed && Flag.isUseOperandTiedToDef(DefIdx)) IsTiedToChangedOp = OpChanged[DefIdx]; if (!Flag.isRegUseKind() && !Flag.isRegDefKind() && !Flag.isRegDefEarlyClobberKind()) continue; unsigned RC; const bool HasRC = Flag.hasRegClassConstraint(RC); if ((!IsTiedToChangedOp && (!HasRC || RC != SP::IntRegsRegClassID)) || NumRegs != 2) continue; assert((i+2 < NumOps) && "Invalid number of operands in inline asm"); SDValue V0 = N->getOperand(i+1); SDValue V1 = N->getOperand(i+2); Register Reg0 = cast(V0)->getReg(); Register Reg1 = cast(V1)->getReg(); SDValue PairedReg; MachineRegisterInfo &MRI = MF->getRegInfo(); if (Flag.isRegDefKind() || Flag.isRegDefEarlyClobberKind()) { // Replace the two GPRs with 1 GPRPair and copy values from GPRPair to // the original GPRs. Register GPVR = MRI.createVirtualRegister(&SP::IntPairRegClass); PairedReg = CurDAG->getRegister(GPVR, MVT::v2i32); SDValue Chain = SDValue(N,0); SDNode *GU = N->getGluedUser(); SDValue RegCopy = CurDAG->getCopyFromReg(Chain, dl, GPVR, MVT::v2i32, Chain.getValue(1)); // Extract values from a GPRPair reg and copy to the original GPR reg. SDValue Sub0 = CurDAG->getTargetExtractSubreg(SP::sub_even, dl, MVT::i32, RegCopy); SDValue Sub1 = CurDAG->getTargetExtractSubreg(SP::sub_odd, dl, MVT::i32, RegCopy); SDValue T0 = CurDAG->getCopyToReg(Sub0, dl, Reg0, Sub0, RegCopy.getValue(1)); SDValue T1 = CurDAG->getCopyToReg(Sub1, dl, Reg1, Sub1, T0.getValue(1)); // Update the original glue user. std::vector Ops(GU->op_begin(), GU->op_end()-1); Ops.push_back(T1.getValue(1)); CurDAG->UpdateNodeOperands(GU, Ops); } else { // For Kind == InlineAsm::Kind::RegUse, we first copy two GPRs into a // GPRPair and then pass the GPRPair to the inline asm. SDValue Chain = AsmNodeOperands[InlineAsm::Op_InputChain]; // As REG_SEQ doesn't take RegisterSDNode, we copy them first. SDValue T0 = CurDAG->getCopyFromReg(Chain, dl, Reg0, MVT::i32, Chain.getValue(1)); SDValue T1 = CurDAG->getCopyFromReg(Chain, dl, Reg1, MVT::i32, T0.getValue(1)); SDValue Pair = SDValue( CurDAG->getMachineNode( TargetOpcode::REG_SEQUENCE, dl, MVT::v2i32, { CurDAG->getTargetConstant(SP::IntPairRegClassID, dl, MVT::i32), T0, CurDAG->getTargetConstant(SP::sub_even, dl, MVT::i32), T1, CurDAG->getTargetConstant(SP::sub_odd, dl, MVT::i32), }), 0); // Copy REG_SEQ into a GPRPair-typed VR and replace the original two // i32 VRs of inline asm with it. Register GPVR = MRI.createVirtualRegister(&SP::IntPairRegClass); PairedReg = CurDAG->getRegister(GPVR, MVT::v2i32); Chain = CurDAG->getCopyToReg(T1, dl, GPVR, Pair, T1.getValue(1)); AsmNodeOperands[InlineAsm::Op_InputChain] = Chain; Glue = Chain.getValue(1); } Changed = true; if(PairedReg.getNode()) { OpChanged[OpChanged.size() -1 ] = true; Flag = InlineAsm::Flag(Flag.getKind(), 1 /* RegNum*/); if (IsTiedToChangedOp) Flag.setMatchingOp(DefIdx); else Flag.setRegClass(SP::IntPairRegClassID); // Replace the current flag. AsmNodeOperands[AsmNodeOperands.size() -1] = CurDAG->getTargetConstant( Flag, dl, MVT::i32); // Add the new register node and skip the original two GPRs. AsmNodeOperands.push_back(PairedReg); // Skip the next two GPRs. i += 2; } } if (Glue.getNode()) AsmNodeOperands.push_back(Glue); if (!Changed) return false; SelectInlineAsmMemoryOperands(AsmNodeOperands, SDLoc(N)); SDValue New = CurDAG->getNode(N->getOpcode(), SDLoc(N), CurDAG->getVTList(MVT::Other, MVT::Glue), AsmNodeOperands); New->setNodeId(-1); ReplaceNode(N, New.getNode()); return true; } void SparcDAGToDAGISel::Select(SDNode *N) { SDLoc dl(N); if (N->isMachineOpcode()) { N->setNodeId(-1); return; // Already selected. } switch (N->getOpcode()) { default: break; case ISD::INLINEASM: case ISD::INLINEASM_BR: { if (tryInlineAsm(N)) return; break; } case SPISD::GLOBAL_BASE_REG: ReplaceNode(N, getGlobalBaseReg()); return; case ISD::SDIV: case ISD::UDIV: { // sdivx / udivx handle 64-bit divides. if (N->getValueType(0) == MVT::i64) break; // FIXME: should use a custom expander to expose the SRA to the dag. SDValue DivLHS = N->getOperand(0); SDValue DivRHS = N->getOperand(1); // Set the Y register to the high-part. SDValue TopPart; if (N->getOpcode() == ISD::SDIV) { TopPart = SDValue(CurDAG->getMachineNode(SP::SRAri, dl, MVT::i32, DivLHS, CurDAG->getTargetConstant(31, dl, MVT::i32)), 0); } else { TopPart = CurDAG->getRegister(SP::G0, MVT::i32); } TopPart = CurDAG->getCopyToReg(CurDAG->getEntryNode(), dl, SP::Y, TopPart, SDValue()) .getValue(1); // FIXME: Handle div by immediate. unsigned Opcode = N->getOpcode() == ISD::SDIV ? SP::SDIVrr : SP::UDIVrr; CurDAG->SelectNodeTo(N, Opcode, MVT::i32, DivLHS, DivRHS, TopPart); return; } } SelectCode(N); } /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for /// inline asm expressions. bool SparcDAGToDAGISel::SelectInlineAsmMemoryOperand( const SDValue &Op, InlineAsm::ConstraintCode ConstraintID, std::vector &OutOps) { SDValue Op0, Op1; switch (ConstraintID) { default: return true; case InlineAsm::ConstraintCode::o: case InlineAsm::ConstraintCode::m: // memory if (!SelectADDRrr(Op, Op0, Op1)) SelectADDRri(Op, Op0, Op1); break; } OutOps.push_back(Op0); OutOps.push_back(Op1); return false; } /// createSparcISelDag - This pass converts a legalized DAG into a /// SPARC-specific DAG, ready for instruction scheduling. /// FunctionPass *llvm::createSparcISelDag(SparcTargetMachine &TM) { return new SparcDAGToDAGISelLegacy(TM); }