Path: blob/main/contrib/llvm-project/llvm/lib/Target/CSKY/CSKYISelDAGToDAG.cpp
35294 views
//===-- CSKYISelDAGToDAG.cpp - A dag to dag inst selector for CSKY---------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines an instruction selector for the CSKY target.9//10//===----------------------------------------------------------------------===//1112#include "CSKY.h"13#include "CSKYSubtarget.h"14#include "CSKYTargetMachine.h"15#include "MCTargetDesc/CSKYMCTargetDesc.h"16#include "llvm/CodeGen/MachineFrameInfo.h"17#include "llvm/CodeGen/SelectionDAG.h"18#include "llvm/CodeGen/SelectionDAGISel.h"1920using namespace llvm;2122#define DEBUG_TYPE "csky-isel"23#define PASS_NAME "CSKY DAG->DAG Pattern Instruction Selection"2425namespace {26class CSKYDAGToDAGISel : public SelectionDAGISel {27const CSKYSubtarget *Subtarget;2829public:30explicit CSKYDAGToDAGISel(CSKYTargetMachine &TM, CodeGenOptLevel OptLevel)31: SelectionDAGISel(TM, OptLevel) {}3233bool runOnMachineFunction(MachineFunction &MF) override {34// Reset the subtarget each time through.35Subtarget = &MF.getSubtarget<CSKYSubtarget>();36SelectionDAGISel::runOnMachineFunction(MF);37return true;38}3940void Select(SDNode *N) override;41bool selectAddCarry(SDNode *N);42bool selectSubCarry(SDNode *N);43bool selectBITCAST_TO_LOHI(SDNode *N);44bool selectInlineAsm(SDNode *N);4546SDNode *createGPRPairNode(EVT VT, SDValue V0, SDValue V1);4748bool SelectInlineAsmMemoryOperand(const SDValue &Op,49InlineAsm::ConstraintCode ConstraintID,50std::vector<SDValue> &OutOps) override;5152#include "CSKYGenDAGISel.inc"53};5455class CSKYDAGToDAGISelLegacy : public SelectionDAGISelLegacy {56public:57static char ID;58explicit CSKYDAGToDAGISelLegacy(CSKYTargetMachine &TM,59CodeGenOptLevel OptLevel)60: SelectionDAGISelLegacy(61ID, std::make_unique<CSKYDAGToDAGISel>(TM, OptLevel)) {}62};63} // namespace6465char CSKYDAGToDAGISelLegacy::ID = 0;6667INITIALIZE_PASS(CSKYDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false)6869void CSKYDAGToDAGISel::Select(SDNode *N) {70// If we have a custom node, we have already selected71if (N->isMachineOpcode()) {72LLVM_DEBUG(dbgs() << "== "; N->dump(CurDAG); dbgs() << "\n");73N->setNodeId(-1);74return;75}7677SDLoc Dl(N);78unsigned Opcode = N->getOpcode();79bool IsSelected = false;8081switch (Opcode) {82default:83break;84case ISD::UADDO_CARRY:85IsSelected = selectAddCarry(N);86break;87case ISD::USUBO_CARRY:88IsSelected = selectSubCarry(N);89break;90case ISD::GLOBAL_OFFSET_TABLE: {91Register GP = Subtarget->getInstrInfo()->getGlobalBaseReg(*MF);92ReplaceNode(N, CurDAG->getRegister(GP, N->getValueType(0)).getNode());9394IsSelected = true;95break;96}97case ISD::FrameIndex: {98SDValue Imm = CurDAG->getTargetConstant(0, Dl, MVT::i32);99int FI = cast<FrameIndexSDNode>(N)->getIndex();100SDValue TFI = CurDAG->getTargetFrameIndex(FI, MVT::i32);101ReplaceNode(N, CurDAG->getMachineNode(Subtarget->hasE2() ? CSKY::ADDI32102: CSKY::ADDI16XZ,103Dl, MVT::i32, TFI, Imm));104105IsSelected = true;106break;107}108case CSKYISD::BITCAST_TO_LOHI:109IsSelected = selectBITCAST_TO_LOHI(N);110break;111case ISD::INLINEASM:112case ISD::INLINEASM_BR:113IsSelected = selectInlineAsm(N);114break;115}116117if (IsSelected)118return;119120// Select the default instruction.121SelectCode(N);122}123124bool CSKYDAGToDAGISel::selectInlineAsm(SDNode *N) {125std::vector<SDValue> AsmNodeOperands;126InlineAsm::Flag Flag;127bool Changed = false;128unsigned NumOps = N->getNumOperands();129130// Normally, i64 data is bounded to two arbitrary GRPs for "%r" constraint.131// However, some instructions (e.g. mula.s32) require GPR pair.132// Since there is no constraint to explicitly specify a133// reg pair, we use GPRPair reg class for "%r" for 64-bit data.134135SDLoc dl(N);136SDValue Glue =137N->getGluedNode() ? N->getOperand(NumOps - 1) : SDValue(nullptr, 0);138139SmallVector<bool, 8> OpChanged;140// Glue node will be appended late.141for (unsigned i = 0, e = N->getGluedNode() ? NumOps - 1 : NumOps; i < e;142++i) {143SDValue op = N->getOperand(i);144AsmNodeOperands.push_back(op);145146if (i < InlineAsm::Op_FirstOperand)147continue;148149if (const auto *C = dyn_cast<ConstantSDNode>(N->getOperand(i)))150Flag = InlineAsm::Flag(C->getZExtValue());151else152continue;153154// Immediate operands to inline asm in the SelectionDAG are modeled with155// two operands. The first is a constant of value InlineAsm::Kind::Imm, and156// the second is a constant with the value of the immediate. If we get here157// and we have a Kind::Imm, skip the next operand, and continue.158if (Flag.isImmKind()) {159SDValue op = N->getOperand(++i);160AsmNodeOperands.push_back(op);161continue;162}163164const unsigned NumRegs = Flag.getNumOperandRegisters();165if (NumRegs)166OpChanged.push_back(false);167168unsigned DefIdx = 0;169bool IsTiedToChangedOp = false;170// If it's a use that is tied with a previous def, it has no171// reg class constraint.172if (Changed && Flag.isUseOperandTiedToDef(DefIdx))173IsTiedToChangedOp = OpChanged[DefIdx];174175// Memory operands to inline asm in the SelectionDAG are modeled with two176// operands: a constant of value InlineAsm::Kind::Mem followed by the input177// operand. If we get here and we have a Kind::Mem, skip the next operand178// (so it doesn't get misinterpreted), and continue. We do this here because179// it's important to update the OpChanged array correctly before moving on.180if (Flag.isMemKind()) {181SDValue op = N->getOperand(++i);182AsmNodeOperands.push_back(op);183continue;184}185186if (!Flag.isRegUseKind() && !Flag.isRegDefKind() &&187!Flag.isRegDefEarlyClobberKind())188continue;189190unsigned RC;191const bool HasRC = Flag.hasRegClassConstraint(RC);192if ((!IsTiedToChangedOp && (!HasRC || RC != CSKY::GPRRegClassID)) ||193NumRegs != 2)194continue;195196assert((i + 2 < NumOps) && "Invalid number of operands in inline asm");197SDValue V0 = N->getOperand(i + 1);198SDValue V1 = N->getOperand(i + 2);199unsigned Reg0 = cast<RegisterSDNode>(V0)->getReg();200unsigned Reg1 = cast<RegisterSDNode>(V1)->getReg();201SDValue PairedReg;202MachineRegisterInfo &MRI = MF->getRegInfo();203204if (Flag.isRegDefKind() || Flag.isRegDefEarlyClobberKind()) {205// Replace the two GPRs with 1 GPRPair and copy values from GPRPair to206// the original GPRs.207208Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass);209PairedReg = CurDAG->getRegister(GPVR, MVT::i64);210SDValue Chain = SDValue(N, 0);211212SDNode *GU = N->getGluedUser();213SDValue RegCopy =214CurDAG->getCopyFromReg(Chain, dl, GPVR, MVT::i64, Chain.getValue(1));215216// Extract values from a GPRPair reg and copy to the original GPR reg.217SDValue Sub0 =218CurDAG->getTargetExtractSubreg(CSKY::sub32_0, dl, MVT::i32, RegCopy);219SDValue Sub1 =220CurDAG->getTargetExtractSubreg(CSKY::sub32_32, dl, MVT::i32, RegCopy);221SDValue T0 =222CurDAG->getCopyToReg(Sub0, dl, Reg0, Sub0, RegCopy.getValue(1));223SDValue T1 = CurDAG->getCopyToReg(Sub1, dl, Reg1, Sub1, T0.getValue(1));224225// Update the original glue user.226std::vector<SDValue> Ops(GU->op_begin(), GU->op_end() - 1);227Ops.push_back(T1.getValue(1));228CurDAG->UpdateNodeOperands(GU, Ops);229} else {230// For Kind == InlineAsm::Kind::RegUse, we first copy two GPRs into a231// GPRPair and then pass the GPRPair to the inline asm.232SDValue Chain = AsmNodeOperands[InlineAsm::Op_InputChain];233234// As REG_SEQ doesn't take RegisterSDNode, we copy them first.235SDValue T0 =236CurDAG->getCopyFromReg(Chain, dl, Reg0, MVT::i32, Chain.getValue(1));237SDValue T1 =238CurDAG->getCopyFromReg(Chain, dl, Reg1, MVT::i32, T0.getValue(1));239SDValue Pair = SDValue(createGPRPairNode(MVT::i64, T0, T1), 0);240241// Copy REG_SEQ into a GPRPair-typed VR and replace the original two242// i32 VRs of inline asm with it.243Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass);244PairedReg = CurDAG->getRegister(GPVR, MVT::i64);245Chain = CurDAG->getCopyToReg(T1, dl, GPVR, Pair, T1.getValue(1));246247AsmNodeOperands[InlineAsm::Op_InputChain] = Chain;248Glue = Chain.getValue(1);249}250251Changed = true;252253if (PairedReg.getNode()) {254OpChanged[OpChanged.size() - 1] = true;255// TODO: maybe a setter for getNumOperandRegisters?256Flag = InlineAsm::Flag(Flag.getKind(), 1 /* RegNum*/);257if (IsTiedToChangedOp)258Flag.setMatchingOp(DefIdx);259else260Flag.setRegClass(CSKY::GPRPairRegClassID);261// Replace the current flag.262AsmNodeOperands[AsmNodeOperands.size() - 1] =263CurDAG->getTargetConstant(Flag, dl, MVT::i32);264// Add the new register node and skip the original two GPRs.265AsmNodeOperands.push_back(PairedReg);266// Skip the next two GPRs.267i += 2;268}269}270271if (Glue.getNode())272AsmNodeOperands.push_back(Glue);273if (!Changed)274return false;275276SDValue New = CurDAG->getNode(N->getOpcode(), SDLoc(N),277CurDAG->getVTList(MVT::Other, MVT::Glue),278AsmNodeOperands);279New->setNodeId(-1);280ReplaceNode(N, New.getNode());281return true;282}283284bool CSKYDAGToDAGISel::selectBITCAST_TO_LOHI(SDNode *N) {285SDLoc Dl(N);286auto VT = N->getValueType(0);287auto V = N->getOperand(0);288289if (!Subtarget->hasFPUv2DoubleFloat())290return false;291292SDValue V1 = SDValue(CurDAG->getMachineNode(CSKY::FMFVRL_D, Dl, VT, V), 0);293SDValue V2 = SDValue(CurDAG->getMachineNode(CSKY::FMFVRH_D, Dl, VT, V), 0);294295ReplaceUses(SDValue(N, 0), V1);296ReplaceUses(SDValue(N, 1), V2);297CurDAG->RemoveDeadNode(N);298299return true;300}301302bool CSKYDAGToDAGISel::selectAddCarry(SDNode *N) {303MachineSDNode *NewNode = nullptr;304auto Type0 = N->getValueType(0);305auto Type1 = N->getValueType(1);306auto Op0 = N->getOperand(0);307auto Op1 = N->getOperand(1);308auto Op2 = N->getOperand(2);309310SDLoc Dl(N);311312if (isNullConstant(Op2)) {313auto *CA = CurDAG->getMachineNode(314Subtarget->has2E3() ? CSKY::CLRC32 : CSKY::CLRC16, Dl, Type1);315NewNode = CurDAG->getMachineNode(316Subtarget->has2E3() ? CSKY::ADDC32 : CSKY::ADDC16, Dl, {Type0, Type1},317{Op0, Op1, SDValue(CA, 0)});318} else if (isOneConstant(Op2)) {319auto *CA = CurDAG->getMachineNode(320Subtarget->has2E3() ? CSKY::SETC32 : CSKY::SETC16, Dl, Type1);321NewNode = CurDAG->getMachineNode(322Subtarget->has2E3() ? CSKY::ADDC32 : CSKY::ADDC16, Dl, {Type0, Type1},323{Op0, Op1, SDValue(CA, 0)});324} else {325NewNode = CurDAG->getMachineNode(Subtarget->has2E3() ? CSKY::ADDC32326: CSKY::ADDC16,327Dl, {Type0, Type1}, {Op0, Op1, Op2});328}329ReplaceNode(N, NewNode);330return true;331}332333static SDValue InvertCarryFlag(const CSKYSubtarget *Subtarget,334SelectionDAG *DAG, SDLoc Dl, SDValue OldCarry) {335auto NewCarryReg =336DAG->getMachineNode(Subtarget->has2E3() ? CSKY::MVCV32 : CSKY::MVCV16, Dl,337MVT::i32, OldCarry);338auto NewCarry =339DAG->getMachineNode(Subtarget->hasE2() ? CSKY::BTSTI32 : CSKY::BTSTI16,340Dl, OldCarry.getValueType(), SDValue(NewCarryReg, 0),341DAG->getTargetConstant(0, Dl, MVT::i32));342return SDValue(NewCarry, 0);343}344345bool CSKYDAGToDAGISel::selectSubCarry(SDNode *N) {346MachineSDNode *NewNode = nullptr;347auto Type0 = N->getValueType(0);348auto Type1 = N->getValueType(1);349auto Op0 = N->getOperand(0);350auto Op1 = N->getOperand(1);351auto Op2 = N->getOperand(2);352353SDLoc Dl(N);354355if (isNullConstant(Op2)) {356auto *CA = CurDAG->getMachineNode(357Subtarget->has2E3() ? CSKY::SETC32 : CSKY::SETC16, Dl, Type1);358NewNode = CurDAG->getMachineNode(359Subtarget->has2E3() ? CSKY::SUBC32 : CSKY::SUBC16, Dl, {Type0, Type1},360{Op0, Op1, SDValue(CA, 0)});361} else if (isOneConstant(Op2)) {362auto *CA = CurDAG->getMachineNode(363Subtarget->has2E3() ? CSKY::CLRC32 : CSKY::CLRC16, Dl, Type1);364NewNode = CurDAG->getMachineNode(365Subtarget->has2E3() ? CSKY::SUBC32 : CSKY::SUBC16, Dl, {Type0, Type1},366{Op0, Op1, SDValue(CA, 0)});367} else {368auto CarryIn = InvertCarryFlag(Subtarget, CurDAG, Dl, Op2);369NewNode = CurDAG->getMachineNode(Subtarget->has2E3() ? CSKY::SUBC32370: CSKY::SUBC16,371Dl, {Type0, Type1}, {Op0, Op1, CarryIn});372}373auto CarryOut = InvertCarryFlag(Subtarget, CurDAG, Dl, SDValue(NewNode, 1));374375ReplaceUses(SDValue(N, 0), SDValue(NewNode, 0));376ReplaceUses(SDValue(N, 1), CarryOut);377CurDAG->RemoveDeadNode(N);378379return true;380}381382SDNode *CSKYDAGToDAGISel::createGPRPairNode(EVT VT, SDValue V0, SDValue V1) {383SDLoc dl(V0.getNode());384SDValue RegClass =385CurDAG->getTargetConstant(CSKY::GPRPairRegClassID, dl, MVT::i32);386SDValue SubReg0 = CurDAG->getTargetConstant(CSKY::sub32_0, dl, MVT::i32);387SDValue SubReg1 = CurDAG->getTargetConstant(CSKY::sub32_32, dl, MVT::i32);388const SDValue Ops[] = {RegClass, V0, SubReg0, V1, SubReg1};389return CurDAG->getMachineNode(TargetOpcode::REG_SEQUENCE, dl, VT, Ops);390}391392bool CSKYDAGToDAGISel::SelectInlineAsmMemoryOperand(393const SDValue &Op, const InlineAsm::ConstraintCode ConstraintID,394std::vector<SDValue> &OutOps) {395switch (ConstraintID) {396case InlineAsm::ConstraintCode::m:397// We just support simple memory operands that have a single address398// operand and need no special handling.399OutOps.push_back(Op);400return false;401default:402break;403}404405return true;406}407408FunctionPass *llvm::createCSKYISelDag(CSKYTargetMachine &TM,409CodeGenOptLevel OptLevel) {410return new CSKYDAGToDAGISelLegacy(TM, OptLevel);411}412413414