| //===-- CSKYISelLowering.cpp - CSKY DAG Lowering Implementation ----------===// |
| // |
| // 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 the interfaces that CSKY uses to lower LLVM code into a |
| // selection DAG. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "CSKYISelLowering.h" |
| #include "CSKYCallingConv.h" |
| #include "CSKYConstantPoolValue.h" |
| #include "CSKYMachineFunctionInfo.h" |
| #include "CSKYRegisterInfo.h" |
| #include "CSKYSubtarget.h" |
| #include "llvm/ADT/Statistic.h" |
| #include "llvm/CodeGen/CallingConvLower.h" |
| #include "llvm/CodeGen/MachineFrameInfo.h" |
| #include "llvm/CodeGen/MachineJumpTableInfo.h" |
| #include "llvm/Support/Debug.h" |
| |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "csky-isel-lowering" |
| |
| STATISTIC(NumTailCalls, "Number of tail calls"); |
| |
| #include "CSKYGenCallingConv.inc" |
| |
| static const MCPhysReg GPRArgRegs[] = {CSKY::R0, CSKY::R1, CSKY::R2, CSKY::R3}; |
| |
| CSKYTargetLowering::CSKYTargetLowering(const TargetMachine &TM, |
| const CSKYSubtarget &STI) |
| : TargetLowering(TM), Subtarget(STI) { |
| // Register Class |
| addRegisterClass(MVT::i32, &CSKY::GPRRegClass); |
| |
| if (STI.useHardFloat()) { |
| if (STI.hasFPUv2SingleFloat()) |
| addRegisterClass(MVT::f32, &CSKY::sFPR32RegClass); |
| else if (STI.hasFPUv3SingleFloat()) |
| addRegisterClass(MVT::f32, &CSKY::FPR32RegClass); |
| |
| if (STI.hasFPUv2DoubleFloat()) |
| addRegisterClass(MVT::f64, &CSKY::sFPR64RegClass); |
| else if (STI.hasFPUv3DoubleFloat()) |
| addRegisterClass(MVT::f64, &CSKY::FPR64RegClass); |
| } |
| |
| setOperationAction(ISD::UADDO_CARRY, MVT::i32, Legal); |
| setOperationAction(ISD::USUBO_CARRY, MVT::i32, Legal); |
| setOperationAction(ISD::BITREVERSE, MVT::i32, Legal); |
| |
| setOperationAction(ISD::SREM, MVT::i32, Expand); |
| setOperationAction(ISD::UREM, MVT::i32, Expand); |
| setOperationAction(ISD::UDIVREM, MVT::i32, Expand); |
| setOperationAction(ISD::SDIVREM, MVT::i32, Expand); |
| setOperationAction(ISD::CTPOP, MVT::i32, Expand); |
| setOperationAction(ISD::ROTR, MVT::i32, Expand); |
| setOperationAction(ISD::SHL_PARTS, MVT::i32, Expand); |
| setOperationAction(ISD::SRL_PARTS, MVT::i32, Expand); |
| setOperationAction(ISD::SRA_PARTS, MVT::i32, Expand); |
| setOperationAction(ISD::UMUL_LOHI, MVT::i32, Expand); |
| setOperationAction(ISD::SMUL_LOHI, MVT::i32, Expand); |
| setOperationAction(ISD::SELECT_CC, MVT::i32, Expand); |
| setOperationAction(ISD::BR_CC, MVT::i32, Expand); |
| setOperationAction(ISD::BR_JT, MVT::Other, Expand); |
| setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i32, Expand); |
| setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); |
| setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); |
| setOperationAction(ISD::MULHS, MVT::i32, Expand); |
| setOperationAction(ISD::MULHU, MVT::i32, Expand); |
| setOperationAction(ISD::VAARG, MVT::Other, Expand); |
| setOperationAction(ISD::VACOPY, MVT::Other, Expand); |
| setOperationAction(ISD::VAEND, MVT::Other, Expand); |
| |
| setLoadExtAction(ISD::EXTLOAD, MVT::i32, MVT::i1, Promote); |
| setLoadExtAction(ISD::SEXTLOAD, MVT::i32, MVT::i1, Promote); |
| setLoadExtAction(ISD::ZEXTLOAD, MVT::i32, MVT::i1, Promote); |
| |
| setOperationAction(ISD::GlobalAddress, MVT::i32, Custom); |
| setOperationAction(ISD::ExternalSymbol, MVT::i32, Custom); |
| setOperationAction(ISD::GlobalTLSAddress, MVT::i32, Custom); |
| setOperationAction(ISD::BlockAddress, MVT::i32, Custom); |
| if (!Subtarget.hasE2()) { |
| setOperationAction(ISD::ConstantPool, MVT::i32, Custom); |
| } |
| setOperationAction(ISD::JumpTable, MVT::i32, Custom); |
| setOperationAction(ISD::VASTART, MVT::Other, Custom); |
| |
| if (!Subtarget.hasE2()) { |
| setLoadExtAction(ISD::SEXTLOAD, MVT::i32, MVT::i8, Expand); |
| setLoadExtAction(ISD::SEXTLOAD, MVT::i32, MVT::i16, Expand); |
| setOperationAction(ISD::CTLZ, MVT::i32, Expand); |
| setOperationAction(ISD::BSWAP, MVT::i32, Expand); |
| } |
| |
| if (!Subtarget.has2E3()) { |
| setOperationAction(ISD::ABS, MVT::i32, Expand); |
| setOperationAction(ISD::BITREVERSE, MVT::i32, Expand); |
| setOperationAction(ISD::CTTZ, MVT::i32, Expand); |
| setOperationAction(ISD::SDIV, MVT::i32, Expand); |
| setOperationAction(ISD::UDIV, MVT::i32, Expand); |
| } |
| |
| setOperationAction(ISD::ATOMIC_FENCE, MVT::Other, Expand); |
| |
| // Float |
| |
| ISD::CondCode FPCCToExtend[] = { |
| ISD::SETONE, ISD::SETUEQ, ISD::SETUGT, |
| ISD::SETUGE, ISD::SETULT, ISD::SETULE, |
| }; |
| |
| ISD::NodeType FPOpToExpand[] = { |
| ISD::FSIN, ISD::FCOS, ISD::FSINCOS, ISD::FPOW, |
| ISD::FREM, ISD::FCOPYSIGN, ISD::FP16_TO_FP, ISD::FP_TO_FP16}; |
| |
| if (STI.useHardFloat()) { |
| |
| MVT AllVTy[] = {MVT::f32, MVT::f64}; |
| |
| for (auto VT : AllVTy) { |
| setOperationAction(ISD::FREM, VT, Expand); |
| setOperationAction(ISD::SELECT_CC, VT, Expand); |
| setOperationAction(ISD::BR_CC, VT, Expand); |
| |
| for (auto CC : FPCCToExtend) |
| setCondCodeAction(CC, VT, Expand); |
| for (auto Op : FPOpToExpand) |
| setOperationAction(Op, VT, Expand); |
| } |
| |
| if (STI.hasFPUv2SingleFloat() || STI.hasFPUv3SingleFloat()) { |
| setOperationAction(ISD::ConstantFP, MVT::f32, Legal); |
| setLoadExtAction(ISD::EXTLOAD, MVT::f32, MVT::f16, Expand); |
| setTruncStoreAction(MVT::f32, MVT::f16, Expand); |
| } |
| if (STI.hasFPUv2DoubleFloat() || STI.hasFPUv3DoubleFloat()) { |
| setLoadExtAction(ISD::EXTLOAD, MVT::f64, MVT::f32, Expand); |
| setTruncStoreAction(MVT::f64, MVT::f32, Expand); |
| setLoadExtAction(ISD::EXTLOAD, MVT::f64, MVT::f16, Expand); |
| setTruncStoreAction(MVT::f64, MVT::f16, Expand); |
| } |
| } |
| |
| // Compute derived properties from the register classes. |
| computeRegisterProperties(STI.getRegisterInfo()); |
| |
| setBooleanContents(UndefinedBooleanContent); |
| setBooleanVectorContents(ZeroOrNegativeOneBooleanContent); |
| |
| // TODO: Add atomic support fully. |
| setMaxAtomicSizeInBitsSupported(0); |
| |
| setStackPointerRegisterToSaveRestore(CSKY::R14); |
| setMinFunctionAlignment(Align(2)); |
| setSchedulingPreference(Sched::Source); |
| } |
| |
| SDValue CSKYTargetLowering::LowerOperation(SDValue Op, |
| SelectionDAG &DAG) const { |
| switch (Op.getOpcode()) { |
| default: |
| llvm_unreachable("unimplemented op"); |
| case ISD::GlobalAddress: |
| return LowerGlobalAddress(Op, DAG); |
| case ISD::ExternalSymbol: |
| return LowerExternalSymbol(Op, DAG); |
| case ISD::GlobalTLSAddress: |
| return LowerGlobalTLSAddress(Op, DAG); |
| case ISD::JumpTable: |
| return LowerJumpTable(Op, DAG); |
| case ISD::BlockAddress: |
| return LowerBlockAddress(Op, DAG); |
| case ISD::ConstantPool: |
| return LowerConstantPool(Op, DAG); |
| case ISD::VASTART: |
| return LowerVASTART(Op, DAG); |
| case ISD::FRAMEADDR: |
| return LowerFRAMEADDR(Op, DAG); |
| case ISD::RETURNADDR: |
| return LowerRETURNADDR(Op, DAG); |
| } |
| } |
| |
| EVT CSKYTargetLowering::getSetCCResultType(const DataLayout &DL, |
| LLVMContext &Context, EVT VT) const { |
| if (!VT.isVector()) |
| return MVT::i32; |
| |
| return VT.changeVectorElementTypeToInteger(); |
| } |
| |
| static SDValue convertValVTToLocVT(SelectionDAG &DAG, SDValue Val, |
| const CCValAssign &VA, const SDLoc &DL) { |
| EVT LocVT = VA.getLocVT(); |
| |
| switch (VA.getLocInfo()) { |
| default: |
| llvm_unreachable("Unexpected CCValAssign::LocInfo"); |
| case CCValAssign::Full: |
| break; |
| case CCValAssign::BCvt: |
| Val = DAG.getNode(ISD::BITCAST, DL, LocVT, Val); |
| break; |
| } |
| return Val; |
| } |
| |
| static SDValue convertLocVTToValVT(SelectionDAG &DAG, SDValue Val, |
| const CCValAssign &VA, const SDLoc &DL) { |
| switch (VA.getLocInfo()) { |
| default: |
| llvm_unreachable("Unexpected CCValAssign::LocInfo"); |
| case CCValAssign::Full: |
| break; |
| case CCValAssign::BCvt: |
| Val = DAG.getNode(ISD::BITCAST, DL, VA.getValVT(), Val); |
| break; |
| } |
| return Val; |
| } |
| |
| static SDValue unpackFromRegLoc(const CSKYSubtarget &Subtarget, |
| SelectionDAG &DAG, SDValue Chain, |
| const CCValAssign &VA, const SDLoc &DL) { |
| MachineFunction &MF = DAG.getMachineFunction(); |
| MachineRegisterInfo &RegInfo = MF.getRegInfo(); |
| EVT LocVT = VA.getLocVT(); |
| SDValue Val; |
| const TargetRegisterClass *RC; |
| |
| switch (LocVT.getSimpleVT().SimpleTy) { |
| default: |
| llvm_unreachable("Unexpected register type"); |
| case MVT::i32: |
| RC = &CSKY::GPRRegClass; |
| break; |
| case MVT::f32: |
| RC = Subtarget.hasFPUv2SingleFloat() ? &CSKY::sFPR32RegClass |
| : &CSKY::FPR32RegClass; |
| break; |
| case MVT::f64: |
| RC = Subtarget.hasFPUv2DoubleFloat() ? &CSKY::sFPR64RegClass |
| : &CSKY::FPR64RegClass; |
| break; |
| } |
| |
| Register VReg = RegInfo.createVirtualRegister(RC); |
| RegInfo.addLiveIn(VA.getLocReg(), VReg); |
| Val = DAG.getCopyFromReg(Chain, DL, VReg, LocVT); |
| |
| return convertLocVTToValVT(DAG, Val, VA, DL); |
| } |
| |
| static SDValue unpackFromMemLoc(SelectionDAG &DAG, SDValue Chain, |
| const CCValAssign &VA, const SDLoc &DL) { |
| MachineFunction &MF = DAG.getMachineFunction(); |
| MachineFrameInfo &MFI = MF.getFrameInfo(); |
| EVT LocVT = VA.getLocVT(); |
| EVT ValVT = VA.getValVT(); |
| EVT PtrVT = MVT::getIntegerVT(DAG.getDataLayout().getPointerSizeInBits(0)); |
| int FI = MFI.CreateFixedObject(ValVT.getSizeInBits() / 8, |
| VA.getLocMemOffset(), /*Immutable=*/true); |
| SDValue FIN = DAG.getFrameIndex(FI, PtrVT); |
| SDValue Val; |
| |
| ISD::LoadExtType ExtType; |
| switch (VA.getLocInfo()) { |
| default: |
| llvm_unreachable("Unexpected CCValAssign::LocInfo"); |
| case CCValAssign::Full: |
| case CCValAssign::BCvt: |
| ExtType = ISD::NON_EXTLOAD; |
| break; |
| } |
| Val = DAG.getExtLoad( |
| ExtType, DL, LocVT, Chain, FIN, |
| MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI), ValVT); |
| return Val; |
| } |
| |
| static SDValue unpack64(SelectionDAG &DAG, SDValue Chain, const CCValAssign &VA, |
| const SDLoc &DL) { |
| assert(VA.getLocVT() == MVT::i32 && |
| (VA.getValVT() == MVT::f64 || VA.getValVT() == MVT::i64) && |
| "Unexpected VA"); |
| MachineFunction &MF = DAG.getMachineFunction(); |
| MachineFrameInfo &MFI = MF.getFrameInfo(); |
| MachineRegisterInfo &RegInfo = MF.getRegInfo(); |
| |
| if (VA.isMemLoc()) { |
| // f64/i64 is passed on the stack. |
| int FI = MFI.CreateFixedObject(8, VA.getLocMemOffset(), /*Immutable=*/true); |
| SDValue FIN = DAG.getFrameIndex(FI, MVT::i32); |
| return DAG.getLoad(VA.getValVT(), DL, Chain, FIN, |
| MachinePointerInfo::getFixedStack(MF, FI)); |
| } |
| |
| assert(VA.isRegLoc() && "Expected register VA assignment"); |
| |
| Register LoVReg = RegInfo.createVirtualRegister(&CSKY::GPRRegClass); |
| RegInfo.addLiveIn(VA.getLocReg(), LoVReg); |
| SDValue Lo = DAG.getCopyFromReg(Chain, DL, LoVReg, MVT::i32); |
| SDValue Hi; |
| if (VA.getLocReg() == CSKY::R3) { |
| // Second half of f64/i64 is passed on the stack. |
| int FI = MFI.CreateFixedObject(4, 0, /*Immutable=*/true); |
| SDValue FIN = DAG.getFrameIndex(FI, MVT::i32); |
| Hi = DAG.getLoad(MVT::i32, DL, Chain, FIN, |
| MachinePointerInfo::getFixedStack(MF, FI)); |
| } else { |
| // Second half of f64/i64 is passed in another GPR. |
| Register HiVReg = RegInfo.createVirtualRegister(&CSKY::GPRRegClass); |
| RegInfo.addLiveIn(VA.getLocReg() + 1, HiVReg); |
| Hi = DAG.getCopyFromReg(Chain, DL, HiVReg, MVT::i32); |
| } |
| return DAG.getNode(CSKYISD::BITCAST_FROM_LOHI, DL, VA.getValVT(), Lo, Hi); |
| } |
| |
| // Transform physical registers into virtual registers. |
| SDValue CSKYTargetLowering::LowerFormalArguments( |
| SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, |
| const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL, |
| SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const { |
| |
| switch (CallConv) { |
| default: |
| report_fatal_error("Unsupported calling convention"); |
| case CallingConv::C: |
| case CallingConv::Fast: |
| break; |
| } |
| |
| MachineFunction &MF = DAG.getMachineFunction(); |
| |
| // Used with vargs to acumulate store chains. |
| std::vector<SDValue> OutChains; |
| |
| // Assign locations to all of the incoming arguments. |
| SmallVector<CCValAssign, 16> ArgLocs; |
| CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); |
| |
| CCInfo.AnalyzeFormalArguments(Ins, CCAssignFnForCall(CallConv, IsVarArg)); |
| |
| for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { |
| CCValAssign &VA = ArgLocs[i]; |
| SDValue ArgValue; |
| |
| bool IsF64OnCSKY = VA.getLocVT() == MVT::i32 && VA.getValVT() == MVT::f64; |
| |
| if (IsF64OnCSKY) |
| ArgValue = unpack64(DAG, Chain, VA, DL); |
| else if (VA.isRegLoc()) |
| ArgValue = unpackFromRegLoc(Subtarget, DAG, Chain, VA, DL); |
| else |
| ArgValue = unpackFromMemLoc(DAG, Chain, VA, DL); |
| |
| InVals.push_back(ArgValue); |
| } |
| |
| if (IsVarArg) { |
| const unsigned XLenInBytes = 4; |
| const MVT XLenVT = MVT::i32; |
| |
| ArrayRef<MCPhysReg> ArgRegs = ArrayRef(GPRArgRegs); |
| unsigned Idx = CCInfo.getFirstUnallocated(ArgRegs); |
| const TargetRegisterClass *RC = &CSKY::GPRRegClass; |
| MachineFrameInfo &MFI = MF.getFrameInfo(); |
| MachineRegisterInfo &RegInfo = MF.getRegInfo(); |
| CSKYMachineFunctionInfo *CSKYFI = MF.getInfo<CSKYMachineFunctionInfo>(); |
| |
| // Offset of the first variable argument from stack pointer, and size of |
| // the vararg save area. For now, the varargs save area is either zero or |
| // large enough to hold a0-a4. |
| int VaArgOffset, VarArgsSaveSize; |
| |
| // If all registers are allocated, then all varargs must be passed on the |
| // stack and we don't need to save any argregs. |
| if (ArgRegs.size() == Idx) { |
| VaArgOffset = CCInfo.getStackSize(); |
| VarArgsSaveSize = 0; |
| } else { |
| VarArgsSaveSize = XLenInBytes * (ArgRegs.size() - Idx); |
| VaArgOffset = -VarArgsSaveSize; |
| } |
| |
| // Record the frame index of the first variable argument |
| // which is a value necessary to VASTART. |
| int FI = MFI.CreateFixedObject(XLenInBytes, VaArgOffset, true); |
| CSKYFI->setVarArgsFrameIndex(FI); |
| |
| // Copy the integer registers that may have been used for passing varargs |
| // to the vararg save area. |
| for (unsigned I = Idx; I < ArgRegs.size(); |
| ++I, VaArgOffset += XLenInBytes) { |
| const Register Reg = RegInfo.createVirtualRegister(RC); |
| RegInfo.addLiveIn(ArgRegs[I], Reg); |
| SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, XLenVT); |
| FI = MFI.CreateFixedObject(XLenInBytes, VaArgOffset, true); |
| SDValue PtrOff = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); |
| SDValue Store = DAG.getStore(Chain, DL, ArgValue, PtrOff, |
| MachinePointerInfo::getFixedStack(MF, FI)); |
| cast<StoreSDNode>(Store.getNode()) |
| ->getMemOperand() |
| ->setValue((Value *)nullptr); |
| OutChains.push_back(Store); |
| } |
| CSKYFI->setVarArgsSaveSize(VarArgsSaveSize); |
| } |
| |
| // All stores are grouped in one node to allow the matching between |
| // the size of Ins and InVals. This only happens for vararg functions. |
| if (!OutChains.empty()) { |
| OutChains.push_back(Chain); |
| Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, OutChains); |
| } |
| |
| return Chain; |
| } |
| |
| bool CSKYTargetLowering::CanLowerReturn( |
| CallingConv::ID CallConv, MachineFunction &MF, bool IsVarArg, |
| const SmallVectorImpl<ISD::OutputArg> &Outs, LLVMContext &Context) const { |
| SmallVector<CCValAssign, 16> CSKYLocs; |
| CCState CCInfo(CallConv, IsVarArg, MF, CSKYLocs, Context); |
| return CCInfo.CheckReturn(Outs, CCAssignFnForReturn(CallConv, IsVarArg)); |
| } |
| |
| SDValue |
| CSKYTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, |
| bool IsVarArg, |
| const SmallVectorImpl<ISD::OutputArg> &Outs, |
| const SmallVectorImpl<SDValue> &OutVals, |
| const SDLoc &DL, SelectionDAG &DAG) const { |
| // Stores the assignment of the return value to a location. |
| SmallVector<CCValAssign, 16> CSKYLocs; |
| |
| // Info about the registers and stack slot. |
| CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), CSKYLocs, |
| *DAG.getContext()); |
| CCInfo.AnalyzeReturn(Outs, CCAssignFnForReturn(CallConv, IsVarArg)); |
| |
| SDValue Glue; |
| SmallVector<SDValue, 4> RetOps(1, Chain); |
| |
| // Copy the result values into the output registers. |
| for (unsigned i = 0, e = CSKYLocs.size(); i < e; ++i) { |
| SDValue Val = OutVals[i]; |
| CCValAssign &VA = CSKYLocs[i]; |
| assert(VA.isRegLoc() && "Can only return in registers!"); |
| |
| bool IsF64OnCSKY = VA.getLocVT() == MVT::i32 && VA.getValVT() == MVT::f64; |
| |
| if (IsF64OnCSKY) { |
| |
| assert(VA.isRegLoc() && "Expected return via registers"); |
| SDValue Split64 = DAG.getNode(CSKYISD::BITCAST_TO_LOHI, DL, |
| DAG.getVTList(MVT::i32, MVT::i32), Val); |
| SDValue Lo = Split64.getValue(0); |
| SDValue Hi = Split64.getValue(1); |
| |
| Register RegLo = VA.getLocReg(); |
| assert(RegLo < CSKY::R31 && "Invalid register pair"); |
| Register RegHi = RegLo + 1; |
| |
| Chain = DAG.getCopyToReg(Chain, DL, RegLo, Lo, Glue); |
| Glue = Chain.getValue(1); |
| RetOps.push_back(DAG.getRegister(RegLo, MVT::i32)); |
| Chain = DAG.getCopyToReg(Chain, DL, RegHi, Hi, Glue); |
| Glue = Chain.getValue(1); |
| RetOps.push_back(DAG.getRegister(RegHi, MVT::i32)); |
| } else { |
| // Handle a 'normal' return. |
| Val = convertValVTToLocVT(DAG, Val, VA, DL); |
| Chain = DAG.getCopyToReg(Chain, DL, VA.getLocReg(), Val, Glue); |
| |
| // Guarantee that all emitted copies are stuck together. |
| Glue = Chain.getValue(1); |
| RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT())); |
| } |
| } |
| |
| RetOps[0] = Chain; // Update chain. |
| |
| // Add the glue node if we have it. |
| if (Glue.getNode()) { |
| RetOps.push_back(Glue); |
| } |
| |
| // Interrupt service routines use different return instructions. |
| if (DAG.getMachineFunction().getFunction().hasFnAttribute("interrupt")) |
| return DAG.getNode(CSKYISD::NIR, DL, MVT::Other, RetOps); |
| |
| return DAG.getNode(CSKYISD::RET, DL, MVT::Other, RetOps); |
| } |
| |
| // Lower a call to a callseq_start + CALL + callseq_end chain, and add input |
| // and output parameter nodes. |
| SDValue CSKYTargetLowering::LowerCall(CallLoweringInfo &CLI, |
| SmallVectorImpl<SDValue> &InVals) const { |
| SelectionDAG &DAG = CLI.DAG; |
| SDLoc &DL = CLI.DL; |
| SmallVectorImpl<ISD::OutputArg> &Outs = CLI.Outs; |
| SmallVectorImpl<SDValue> &OutVals = CLI.OutVals; |
| SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins; |
| SDValue Chain = CLI.Chain; |
| SDValue Callee = CLI.Callee; |
| bool &IsTailCall = CLI.IsTailCall; |
| CallingConv::ID CallConv = CLI.CallConv; |
| bool IsVarArg = CLI.IsVarArg; |
| EVT PtrVT = getPointerTy(DAG.getDataLayout()); |
| MVT XLenVT = MVT::i32; |
| |
| MachineFunction &MF = DAG.getMachineFunction(); |
| |
| // Analyze the operands of the call, assigning locations to each operand. |
| SmallVector<CCValAssign, 16> ArgLocs; |
| CCState ArgCCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); |
| |
| ArgCCInfo.AnalyzeCallOperands(Outs, CCAssignFnForCall(CallConv, IsVarArg)); |
| |
| // Check if it's really possible to do a tail call. |
| if (IsTailCall) |
| IsTailCall = false; // TODO: TailCallOptimization; |
| |
| if (IsTailCall) |
| ++NumTailCalls; |
| else if (CLI.CB && CLI.CB->isMustTailCall()) |
| report_fatal_error("failed to perform tail call elimination on a call " |
| "site marked musttail"); |
| |
| // Get a count of how many bytes are to be pushed on the stack. |
| unsigned NumBytes = ArgCCInfo.getStackSize(); |
| |
| // Create local copies for byval args |
| SmallVector<SDValue, 8> ByValArgs; |
| for (unsigned i = 0, e = Outs.size(); i != e; ++i) { |
| ISD::ArgFlagsTy Flags = Outs[i].Flags; |
| if (!Flags.isByVal()) |
| continue; |
| |
| SDValue Arg = OutVals[i]; |
| unsigned Size = Flags.getByValSize(); |
| Align Alignment = Flags.getNonZeroByValAlign(); |
| |
| int FI = |
| MF.getFrameInfo().CreateStackObject(Size, Alignment, /*isSS=*/false); |
| SDValue FIPtr = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); |
| SDValue SizeNode = DAG.getConstant(Size, DL, XLenVT); |
| |
| Chain = DAG.getMemcpy(Chain, DL, FIPtr, Arg, SizeNode, Alignment, |
| /*IsVolatile=*/false, |
| /*AlwaysInline=*/false, IsTailCall, |
| MachinePointerInfo(), MachinePointerInfo()); |
| ByValArgs.push_back(FIPtr); |
| } |
| |
| if (!IsTailCall) |
| Chain = DAG.getCALLSEQ_START(Chain, NumBytes, 0, CLI.DL); |
| |
| // Copy argument values to their designated locations. |
| SmallVector<std::pair<Register, SDValue>, 8> RegsToPass; |
| SmallVector<SDValue, 8> MemOpChains; |
| SDValue StackPtr; |
| for (unsigned i = 0, j = 0, e = ArgLocs.size(); i != e; ++i) { |
| CCValAssign &VA = ArgLocs[i]; |
| SDValue ArgValue = OutVals[i]; |
| ISD::ArgFlagsTy Flags = Outs[i].Flags; |
| |
| bool IsF64OnCSKY = VA.getLocVT() == MVT::i32 && VA.getValVT() == MVT::f64; |
| |
| if (IsF64OnCSKY && VA.isRegLoc()) { |
| SDValue Split64 = |
| DAG.getNode(CSKYISD::BITCAST_TO_LOHI, DL, |
| DAG.getVTList(MVT::i32, MVT::i32), ArgValue); |
| SDValue Lo = Split64.getValue(0); |
| SDValue Hi = Split64.getValue(1); |
| |
| Register RegLo = VA.getLocReg(); |
| RegsToPass.push_back(std::make_pair(RegLo, Lo)); |
| |
| if (RegLo == CSKY::R3) { |
| // Second half of f64/i64 is passed on the stack. |
| // Work out the address of the stack slot. |
| if (!StackPtr.getNode()) |
| StackPtr = DAG.getCopyFromReg(Chain, DL, CSKY::R14, PtrVT); |
| // Emit the store. |
| MemOpChains.push_back( |
| DAG.getStore(Chain, DL, Hi, StackPtr, MachinePointerInfo())); |
| } else { |
| // Second half of f64/i64 is passed in another GPR. |
| assert(RegLo < CSKY::R31 && "Invalid register pair"); |
| Register RegHigh = RegLo + 1; |
| RegsToPass.push_back(std::make_pair(RegHigh, Hi)); |
| } |
| continue; |
| } |
| |
| ArgValue = convertValVTToLocVT(DAG, ArgValue, VA, DL); |
| |
| // Use local copy if it is a byval arg. |
| if (Flags.isByVal()) |
| ArgValue = ByValArgs[j++]; |
| |
| if (VA.isRegLoc()) { |
| // Queue up the argument copies and emit them at the end. |
| RegsToPass.push_back(std::make_pair(VA.getLocReg(), ArgValue)); |
| } else { |
| assert(VA.isMemLoc() && "Argument not register or memory"); |
| assert(!IsTailCall && "Tail call not allowed if stack is used " |
| "for passing parameters"); |
| |
| // Work out the address of the stack slot. |
| if (!StackPtr.getNode()) |
| StackPtr = DAG.getCopyFromReg(Chain, DL, CSKY::R14, PtrVT); |
| SDValue Address = |
| DAG.getNode(ISD::ADD, DL, PtrVT, StackPtr, |
| DAG.getIntPtrConstant(VA.getLocMemOffset(), DL)); |
| |
| // Emit the store. |
| MemOpChains.push_back( |
| DAG.getStore(Chain, DL, ArgValue, Address, MachinePointerInfo())); |
| } |
| } |
| |
| // Join the stores, which are independent of one another. |
| if (!MemOpChains.empty()) |
| Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, MemOpChains); |
| |
| SDValue Glue; |
| |
| // Build a sequence of copy-to-reg nodes, chained and glued together. |
| for (auto &Reg : RegsToPass) { |
| Chain = DAG.getCopyToReg(Chain, DL, Reg.first, Reg.second, Glue); |
| Glue = Chain.getValue(1); |
| } |
| |
| SmallVector<SDValue, 8> Ops; |
| EVT Ty = getPointerTy(DAG.getDataLayout()); |
| bool IsRegCall = false; |
| |
| Ops.push_back(Chain); |
| |
| if (GlobalAddressSDNode *S = dyn_cast<GlobalAddressSDNode>(Callee)) { |
| const GlobalValue *GV = S->getGlobal(); |
| bool IsLocal = |
| getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV); |
| |
| if (isPositionIndependent() || !Subtarget.has2E3()) { |
| IsRegCall = true; |
| Ops.push_back(getAddr<GlobalAddressSDNode, true>(S, DAG, IsLocal)); |
| } else { |
| Ops.push_back(getTargetNode(cast<GlobalAddressSDNode>(Callee), DL, Ty, |
| DAG, CSKYII::MO_None)); |
| Ops.push_back(getTargetConstantPoolValue( |
| cast<GlobalAddressSDNode>(Callee), Ty, DAG, CSKYII::MO_None)); |
| } |
| } else if (ExternalSymbolSDNode *S = dyn_cast<ExternalSymbolSDNode>(Callee)) { |
| bool IsLocal = getTargetMachine().shouldAssumeDSOLocal( |
| *MF.getFunction().getParent(), nullptr); |
| |
| if (isPositionIndependent() || !Subtarget.has2E3()) { |
| IsRegCall = true; |
| Ops.push_back(getAddr<ExternalSymbolSDNode, true>(S, DAG, IsLocal)); |
| } else { |
| Ops.push_back(getTargetNode(cast<ExternalSymbolSDNode>(Callee), DL, Ty, |
| DAG, CSKYII::MO_None)); |
| Ops.push_back(getTargetConstantPoolValue( |
| cast<ExternalSymbolSDNode>(Callee), Ty, DAG, CSKYII::MO_None)); |
| } |
| } else { |
| IsRegCall = true; |
| Ops.push_back(Callee); |
| } |
| |
| // Add argument registers to the end of the list so that they are |
| // known live into the call. |
| for (auto &Reg : RegsToPass) |
| Ops.push_back(DAG.getRegister(Reg.first, Reg.second.getValueType())); |
| |
| if (!IsTailCall) { |
| // Add a register mask operand representing the call-preserved registers. |
| const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo(); |
| const uint32_t *Mask = TRI->getCallPreservedMask(MF, CallConv); |
| assert(Mask && "Missing call preserved mask for calling convention"); |
| Ops.push_back(DAG.getRegisterMask(Mask)); |
| } |
| |
| // Glue the call to the argument copies, if any. |
| if (Glue.getNode()) |
| Ops.push_back(Glue); |
| |
| // Emit the call. |
| SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); |
| |
| if (IsTailCall) { |
| MF.getFrameInfo().setHasTailCall(); |
| return DAG.getNode(IsRegCall ? CSKYISD::TAILReg : CSKYISD::TAIL, DL, |
| NodeTys, Ops); |
| } |
| |
| Chain = DAG.getNode(IsRegCall ? CSKYISD::CALLReg : CSKYISD::CALL, DL, NodeTys, |
| Ops); |
| DAG.addNoMergeSiteInfo(Chain.getNode(), CLI.NoMerge); |
| Glue = Chain.getValue(1); |
| |
| // Mark the end of the call, which is glued to the call itself. |
| Chain = DAG.getCALLSEQ_END(Chain, NumBytes, 0, Glue, DL); |
| Glue = Chain.getValue(1); |
| |
| // Assign locations to each value returned by this call. |
| SmallVector<CCValAssign, 16> CSKYLocs; |
| CCState RetCCInfo(CallConv, IsVarArg, MF, CSKYLocs, *DAG.getContext()); |
| RetCCInfo.AnalyzeCallResult(Ins, CCAssignFnForReturn(CallConv, IsVarArg)); |
| |
| // Copy all of the result registers out of their specified physreg. |
| for (auto &VA : CSKYLocs) { |
| // Copy the value out |
| SDValue RetValue = |
| DAG.getCopyFromReg(Chain, DL, VA.getLocReg(), VA.getLocVT(), Glue); |
| // Glue the RetValue to the end of the call sequence |
| Chain = RetValue.getValue(1); |
| Glue = RetValue.getValue(2); |
| |
| bool IsF64OnCSKY = VA.getLocVT() == MVT::i32 && VA.getValVT() == MVT::f64; |
| |
| if (IsF64OnCSKY) { |
| assert(VA.getLocReg() == GPRArgRegs[0] && "Unexpected reg assignment"); |
| SDValue RetValue2 = |
| DAG.getCopyFromReg(Chain, DL, GPRArgRegs[1], MVT::i32, Glue); |
| Chain = RetValue2.getValue(1); |
| Glue = RetValue2.getValue(2); |
| RetValue = DAG.getNode(CSKYISD::BITCAST_FROM_LOHI, DL, VA.getValVT(), |
| RetValue, RetValue2); |
| } |
| |
| RetValue = convertLocVTToValVT(DAG, RetValue, VA, DL); |
| |
| InVals.push_back(RetValue); |
| } |
| |
| return Chain; |
| } |
| |
| CCAssignFn *CSKYTargetLowering::CCAssignFnForReturn(CallingConv::ID CC, |
| bool IsVarArg) const { |
| if (IsVarArg || !Subtarget.useHardFloatABI()) |
| return RetCC_CSKY_ABIV2_SOFT; |
| else |
| return RetCC_CSKY_ABIV2_FP; |
| } |
| |
| CCAssignFn *CSKYTargetLowering::CCAssignFnForCall(CallingConv::ID CC, |
| bool IsVarArg) const { |
| if (IsVarArg || !Subtarget.useHardFloatABI()) |
| return CC_CSKY_ABIV2_SOFT; |
| else |
| return CC_CSKY_ABIV2_FP; |
| } |
| |
| static CSKYCP::CSKYCPModifier getModifier(unsigned Flags) { |
| |
| if (Flags == CSKYII::MO_ADDR32) |
| return CSKYCP::ADDR; |
| else if (Flags == CSKYII::MO_GOT32) |
| return CSKYCP::GOT; |
| else if (Flags == CSKYII::MO_GOTOFF) |
| return CSKYCP::GOTOFF; |
| else if (Flags == CSKYII::MO_PLT32) |
| return CSKYCP::PLT; |
| else if (Flags == CSKYII::MO_None) |
| return CSKYCP::NO_MOD; |
| else |
| assert(0 && "unknown CSKYII Modifier"); |
| return CSKYCP::NO_MOD; |
| } |
| |
| SDValue CSKYTargetLowering::getTargetConstantPoolValue(GlobalAddressSDNode *N, |
| EVT Ty, |
| SelectionDAG &DAG, |
| unsigned Flags) const { |
| CSKYConstantPoolValue *CPV = CSKYConstantPoolConstant::Create( |
| N->getGlobal(), CSKYCP::CPValue, 0, getModifier(Flags), false); |
| |
| return DAG.getTargetConstantPool(CPV, Ty); |
| } |
| |
| CSKYTargetLowering::ConstraintType |
| CSKYTargetLowering::getConstraintType(StringRef Constraint) const { |
| if (Constraint.size() == 1) { |
| switch (Constraint[0]) { |
| default: |
| break; |
| case 'a': |
| case 'b': |
| case 'v': |
| case 'w': |
| case 'y': |
| return C_RegisterClass; |
| case 'c': |
| case 'l': |
| case 'h': |
| case 'z': |
| return C_Register; |
| } |
| } |
| return TargetLowering::getConstraintType(Constraint); |
| } |
| |
| std::pair<unsigned, const TargetRegisterClass *> |
| CSKYTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, |
| StringRef Constraint, |
| MVT VT) const { |
| if (Constraint.size() == 1) { |
| switch (Constraint[0]) { |
| case 'r': |
| return std::make_pair(0U, &CSKY::GPRRegClass); |
| case 'a': |
| return std::make_pair(0U, &CSKY::mGPRRegClass); |
| case 'b': |
| return std::make_pair(0U, &CSKY::sGPRRegClass); |
| case 'z': |
| return std::make_pair(CSKY::R14, &CSKY::GPRRegClass); |
| case 'c': |
| return std::make_pair(CSKY::C, &CSKY::CARRYRegClass); |
| case 'w': |
| if ((Subtarget.hasFPUv2SingleFloat() || |
| Subtarget.hasFPUv3SingleFloat()) && |
| VT == MVT::f32) |
| return std::make_pair(0U, &CSKY::sFPR32RegClass); |
| if ((Subtarget.hasFPUv2DoubleFloat() || |
| Subtarget.hasFPUv3DoubleFloat()) && |
| VT == MVT::f64) |
| return std::make_pair(0U, &CSKY::sFPR64RegClass); |
| break; |
| case 'v': |
| if (Subtarget.hasFPUv2SingleFloat() && VT == MVT::f32) |
| return std::make_pair(0U, &CSKY::sFPR32RegClass); |
| if (Subtarget.hasFPUv3SingleFloat() && VT == MVT::f32) |
| return std::make_pair(0U, &CSKY::FPR32RegClass); |
| if (Subtarget.hasFPUv2DoubleFloat() && VT == MVT::f64) |
| return std::make_pair(0U, &CSKY::sFPR64RegClass); |
| if (Subtarget.hasFPUv3DoubleFloat() && VT == MVT::f64) |
| return std::make_pair(0U, &CSKY::FPR64RegClass); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| if (Constraint == "{c}") |
| return std::make_pair(CSKY::C, &CSKY::CARRYRegClass); |
| |
| // Clang will correctly decode the usage of register name aliases into their |
| // official names. However, other frontends like `rustc` do not. This allows |
| // users of these frontends to use the ABI names for registers in LLVM-style |
| // register constraints. |
| unsigned XRegFromAlias = StringSwitch<unsigned>(Constraint.lower()) |
| .Case("{a0}", CSKY::R0) |
| .Case("{a1}", CSKY::R1) |
| .Case("{a2}", CSKY::R2) |
| .Case("{a3}", CSKY::R3) |
| .Case("{l0}", CSKY::R4) |
| .Case("{l1}", CSKY::R5) |
| .Case("{l2}", CSKY::R6) |
| .Case("{l3}", CSKY::R7) |
| .Case("{l4}", CSKY::R8) |
| .Case("{l5}", CSKY::R9) |
| .Case("{l6}", CSKY::R10) |
| .Case("{l7}", CSKY::R11) |
| .Case("{t0}", CSKY::R12) |
| .Case("{t1}", CSKY::R13) |
| .Case("{sp}", CSKY::R14) |
| .Case("{lr}", CSKY::R15) |
| .Case("{l8}", CSKY::R16) |
| .Case("{l9}", CSKY::R17) |
| .Case("{t2}", CSKY::R18) |
| .Case("{t3}", CSKY::R19) |
| .Case("{t4}", CSKY::R20) |
| .Case("{t5}", CSKY::R21) |
| .Case("{t6}", CSKY::R22) |
| .Cases("{t7}", "{fp}", CSKY::R23) |
| .Cases("{t8}", "{top}", CSKY::R24) |
| .Cases("{t9}", "{bsp}", CSKY::R25) |
| .Case("{r26}", CSKY::R26) |
| .Case("{r27}", CSKY::R27) |
| .Cases("{gb}", "{rgb}", "{rdb}", CSKY::R28) |
| .Cases("{tb}", "{rtb}", CSKY::R29) |
| .Case("{svbr}", CSKY::R30) |
| .Case("{tls}", CSKY::R31) |
| .Default(CSKY::NoRegister); |
| |
| if (XRegFromAlias != CSKY::NoRegister) |
| return std::make_pair(XRegFromAlias, &CSKY::GPRRegClass); |
| |
| // Since TargetLowering::getRegForInlineAsmConstraint uses the name of the |
| // TableGen record rather than the AsmName to choose registers for InlineAsm |
| // constraints, plus we want to match those names to the widest floating point |
| // register type available, manually select floating point registers here. |
| // |
| // The second case is the ABI name of the register, so that frontends can also |
| // use the ABI names in register constraint lists. |
| if (Subtarget.useHardFloat()) { |
| unsigned FReg = StringSwitch<unsigned>(Constraint.lower()) |
| .Cases("{fr0}", "{vr0}", CSKY::F0_32) |
| .Cases("{fr1}", "{vr1}", CSKY::F1_32) |
| .Cases("{fr2}", "{vr2}", CSKY::F2_32) |
| .Cases("{fr3}", "{vr3}", CSKY::F3_32) |
| .Cases("{fr4}", "{vr4}", CSKY::F4_32) |
| .Cases("{fr5}", "{vr5}", CSKY::F5_32) |
| .Cases("{fr6}", "{vr6}", CSKY::F6_32) |
| .Cases("{fr7}", "{vr7}", CSKY::F7_32) |
| .Cases("{fr8}", "{vr8}", CSKY::F8_32) |
| .Cases("{fr9}", "{vr9}", CSKY::F9_32) |
| .Cases("{fr10}", "{vr10}", CSKY::F10_32) |
| .Cases("{fr11}", "{vr11}", CSKY::F11_32) |
| .Cases("{fr12}", "{vr12}", CSKY::F12_32) |
| .Cases("{fr13}", "{vr13}", CSKY::F13_32) |
| .Cases("{fr14}", "{vr14}", CSKY::F14_32) |
| .Cases("{fr15}", "{vr15}", CSKY::F15_32) |
| .Cases("{fr16}", "{vr16}", CSKY::F16_32) |
| .Cases("{fr17}", "{vr17}", CSKY::F17_32) |
| .Cases("{fr18}", "{vr18}", CSKY::F18_32) |
| .Cases("{fr19}", "{vr19}", CSKY::F19_32) |
| .Cases("{fr20}", "{vr20}", CSKY::F20_32) |
| .Cases("{fr21}", "{vr21}", CSKY::F21_32) |
| .Cases("{fr22}", "{vr22}", CSKY::F22_32) |
| .Cases("{fr23}", "{vr23}", CSKY::F23_32) |
| .Cases("{fr24}", "{vr24}", CSKY::F24_32) |
| .Cases("{fr25}", "{vr25}", CSKY::F25_32) |
| .Cases("{fr26}", "{vr26}", CSKY::F26_32) |
| .Cases("{fr27}", "{vr27}", CSKY::F27_32) |
| .Cases("{fr28}", "{vr28}", CSKY::F28_32) |
| .Cases("{fr29}", "{vr29}", CSKY::F29_32) |
| .Cases("{fr30}", "{vr30}", CSKY::F30_32) |
| .Cases("{fr31}", "{vr31}", CSKY::F31_32) |
| .Default(CSKY::NoRegister); |
| if (FReg != CSKY::NoRegister) { |
| assert(CSKY::F0_32 <= FReg && FReg <= CSKY::F31_32 && "Unknown fp-reg"); |
| unsigned RegNo = FReg - CSKY::F0_32; |
| unsigned DReg = CSKY::F0_64 + RegNo; |
| |
| if (Subtarget.hasFPUv2DoubleFloat()) |
| return std::make_pair(DReg, &CSKY::sFPR64RegClass); |
| else if (Subtarget.hasFPUv3DoubleFloat()) |
| return std::make_pair(DReg, &CSKY::FPR64RegClass); |
| else if (Subtarget.hasFPUv2SingleFloat()) |
| return std::make_pair(FReg, &CSKY::sFPR32RegClass); |
| else if (Subtarget.hasFPUv3SingleFloat()) |
| return std::make_pair(FReg, &CSKY::FPR32RegClass); |
| } |
| } |
| |
| return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); |
| } |
| |
| static MachineBasicBlock * |
| emitSelectPseudo(MachineInstr &MI, MachineBasicBlock *BB, unsigned Opcode) { |
| |
| const TargetInstrInfo &TII = *BB->getParent()->getSubtarget().getInstrInfo(); |
| DebugLoc DL = MI.getDebugLoc(); |
| |
| // To "insert" a SELECT instruction, we actually have to insert the |
| // diamond control-flow pattern. The incoming instruction knows the |
| // destination vreg to set, the condition code register to branch on, the |
| // true/false values to select between, and a branch opcode to use. |
| const BasicBlock *LLVM_BB = BB->getBasicBlock(); |
| MachineFunction::iterator It = ++BB->getIterator(); |
| |
| // thisMBB: |
| // ... |
| // TrueVal = ... |
| // bt32 c, sinkMBB |
| // fallthrough --> copyMBB |
| MachineBasicBlock *thisMBB = BB; |
| MachineFunction *F = BB->getParent(); |
| MachineBasicBlock *copyMBB = F->CreateMachineBasicBlock(LLVM_BB); |
| MachineBasicBlock *sinkMBB = F->CreateMachineBasicBlock(LLVM_BB); |
| F->insert(It, copyMBB); |
| F->insert(It, sinkMBB); |
| |
| // Transfer the remainder of BB and its successor edges to sinkMBB. |
| sinkMBB->splice(sinkMBB->begin(), BB, |
| std::next(MachineBasicBlock::iterator(MI)), BB->end()); |
| sinkMBB->transferSuccessorsAndUpdatePHIs(BB); |
| |
| // Next, add the true and fallthrough blocks as its successors. |
| BB->addSuccessor(copyMBB); |
| BB->addSuccessor(sinkMBB); |
| |
| // bt32 condition, sinkMBB |
| BuildMI(BB, DL, TII.get(Opcode)) |
| .addReg(MI.getOperand(1).getReg()) |
| .addMBB(sinkMBB); |
| |
| // copyMBB: |
| // %FalseValue = ... |
| // # fallthrough to sinkMBB |
| BB = copyMBB; |
| |
| // Update machine-CFG edges |
| BB->addSuccessor(sinkMBB); |
| |
| // sinkMBB: |
| // %Result = phi [ %TrueValue, thisMBB ], [ %FalseValue, copyMBB ] |
| // ... |
| BB = sinkMBB; |
| |
| BuildMI(*BB, BB->begin(), DL, TII.get(CSKY::PHI), MI.getOperand(0).getReg()) |
| .addReg(MI.getOperand(2).getReg()) |
| .addMBB(thisMBB) |
| .addReg(MI.getOperand(3).getReg()) |
| .addMBB(copyMBB); |
| |
| MI.eraseFromParent(); // The pseudo instruction is gone now. |
| |
| return BB; |
| } |
| |
| MachineBasicBlock * |
| CSKYTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, |
| MachineBasicBlock *BB) const { |
| switch (MI.getOpcode()) { |
| default: |
| llvm_unreachable("Unexpected instr type to insert"); |
| case CSKY::FSELS: |
| case CSKY::FSELD: |
| if (Subtarget.hasE2()) |
| return emitSelectPseudo(MI, BB, CSKY::BT32); |
| else |
| return emitSelectPseudo(MI, BB, CSKY::BT16); |
| case CSKY::ISEL32: |
| return emitSelectPseudo(MI, BB, CSKY::BT32); |
| case CSKY::ISEL16: |
| return emitSelectPseudo(MI, BB, CSKY::BT16); |
| } |
| } |
| |
| SDValue CSKYTargetLowering::getTargetConstantPoolValue(ExternalSymbolSDNode *N, |
| EVT Ty, |
| SelectionDAG &DAG, |
| unsigned Flags) const { |
| CSKYConstantPoolValue *CPV = |
| CSKYConstantPoolSymbol::Create(Type::getInt32Ty(*DAG.getContext()), |
| N->getSymbol(), 0, getModifier(Flags)); |
| |
| return DAG.getTargetConstantPool(CPV, Ty); |
| } |
| |
| SDValue CSKYTargetLowering::getTargetConstantPoolValue(JumpTableSDNode *N, |
| EVT Ty, |
| SelectionDAG &DAG, |
| unsigned Flags) const { |
| CSKYConstantPoolValue *CPV = |
| CSKYConstantPoolJT::Create(Type::getInt32Ty(*DAG.getContext()), |
| N->getIndex(), 0, getModifier(Flags)); |
| return DAG.getTargetConstantPool(CPV, Ty); |
| } |
| |
| SDValue CSKYTargetLowering::getTargetConstantPoolValue(BlockAddressSDNode *N, |
| EVT Ty, |
| SelectionDAG &DAG, |
| unsigned Flags) const { |
| assert(N->getOffset() == 0); |
| CSKYConstantPoolValue *CPV = CSKYConstantPoolConstant::Create( |
| N->getBlockAddress(), CSKYCP::CPBlockAddress, 0, getModifier(Flags), |
| false); |
| return DAG.getTargetConstantPool(CPV, Ty); |
| } |
| |
| SDValue CSKYTargetLowering::getTargetConstantPoolValue(ConstantPoolSDNode *N, |
| EVT Ty, |
| SelectionDAG &DAG, |
| unsigned Flags) const { |
| assert(N->getOffset() == 0); |
| CSKYConstantPoolValue *CPV = CSKYConstantPoolConstant::Create( |
| N->getConstVal(), Type::getInt32Ty(*DAG.getContext()), |
| CSKYCP::CPConstPool, 0, getModifier(Flags), false); |
| return DAG.getTargetConstantPool(CPV, Ty); |
| } |
| |
| SDValue CSKYTargetLowering::getTargetNode(GlobalAddressSDNode *N, SDLoc DL, |
| EVT Ty, SelectionDAG &DAG, |
| unsigned Flags) const { |
| return DAG.getTargetGlobalAddress(N->getGlobal(), DL, Ty, 0, Flags); |
| } |
| |
| SDValue CSKYTargetLowering::getTargetNode(ExternalSymbolSDNode *N, SDLoc DL, |
| EVT Ty, SelectionDAG &DAG, |
| unsigned Flags) const { |
| return DAG.getTargetExternalSymbol(N->getSymbol(), Ty, Flags); |
| } |
| |
| SDValue CSKYTargetLowering::getTargetNode(JumpTableSDNode *N, SDLoc DL, EVT Ty, |
| SelectionDAG &DAG, |
| unsigned Flags) const { |
| return DAG.getTargetJumpTable(N->getIndex(), Ty, Flags); |
| } |
| |
| SDValue CSKYTargetLowering::getTargetNode(BlockAddressSDNode *N, SDLoc DL, |
| EVT Ty, SelectionDAG &DAG, |
| unsigned Flags) const { |
| return DAG.getTargetBlockAddress(N->getBlockAddress(), Ty, N->getOffset(), |
| Flags); |
| } |
| |
| SDValue CSKYTargetLowering::getTargetNode(ConstantPoolSDNode *N, SDLoc DL, |
| EVT Ty, SelectionDAG &DAG, |
| unsigned Flags) const { |
| |
| return DAG.getTargetConstantPool(N->getConstVal(), Ty, N->getAlign(), |
| N->getOffset(), Flags); |
| } |
| |
| const char *CSKYTargetLowering::getTargetNodeName(unsigned Opcode) const { |
| switch (Opcode) { |
| default: |
| llvm_unreachable("unknown CSKYISD node"); |
| case CSKYISD::NIE: |
| return "CSKYISD::NIE"; |
| case CSKYISD::NIR: |
| return "CSKYISD::NIR"; |
| case CSKYISD::RET: |
| return "CSKYISD::RET"; |
| case CSKYISD::CALL: |
| return "CSKYISD::CALL"; |
| case CSKYISD::CALLReg: |
| return "CSKYISD::CALLReg"; |
| case CSKYISD::TAIL: |
| return "CSKYISD::TAIL"; |
| case CSKYISD::TAILReg: |
| return "CSKYISD::TAILReg"; |
| case CSKYISD::LOAD_ADDR: |
| return "CSKYISD::LOAD_ADDR"; |
| case CSKYISD::BITCAST_TO_LOHI: |
| return "CSKYISD::BITCAST_TO_LOHI"; |
| case CSKYISD::BITCAST_FROM_LOHI: |
| return "CSKYISD::BITCAST_FROM_LOHI"; |
| } |
| } |
| |
| SDValue CSKYTargetLowering::LowerGlobalAddress(SDValue Op, |
| SelectionDAG &DAG) const { |
| SDLoc DL(Op); |
| EVT Ty = Op.getValueType(); |
| GlobalAddressSDNode *N = cast<GlobalAddressSDNode>(Op); |
| int64_t Offset = N->getOffset(); |
| |
| const GlobalValue *GV = N->getGlobal(); |
| bool IsLocal = getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV); |
| SDValue Addr = getAddr<GlobalAddressSDNode, false>(N, DAG, IsLocal); |
| |
| // In order to maximise the opportunity for common subexpression elimination, |
| // emit a separate ADD node for the global address offset instead of folding |
| // it in the global address node. Later peephole optimisations may choose to |
| // fold it back in when profitable. |
| if (Offset != 0) |
| return DAG.getNode(ISD::ADD, DL, Ty, Addr, |
| DAG.getConstant(Offset, DL, MVT::i32)); |
| return Addr; |
| } |
| |
| SDValue CSKYTargetLowering::LowerExternalSymbol(SDValue Op, |
| SelectionDAG &DAG) const { |
| ExternalSymbolSDNode *N = cast<ExternalSymbolSDNode>(Op); |
| |
| return getAddr(N, DAG, false); |
| } |
| |
| SDValue CSKYTargetLowering::LowerJumpTable(SDValue Op, |
| SelectionDAG &DAG) const { |
| JumpTableSDNode *N = cast<JumpTableSDNode>(Op); |
| |
| return getAddr<JumpTableSDNode, false>(N, DAG); |
| } |
| |
| SDValue CSKYTargetLowering::LowerBlockAddress(SDValue Op, |
| SelectionDAG &DAG) const { |
| BlockAddressSDNode *N = cast<BlockAddressSDNode>(Op); |
| |
| return getAddr(N, DAG); |
| } |
| |
| SDValue CSKYTargetLowering::LowerConstantPool(SDValue Op, |
| SelectionDAG &DAG) const { |
| assert(!Subtarget.hasE2()); |
| ConstantPoolSDNode *N = cast<ConstantPoolSDNode>(Op); |
| |
| return getAddr(N, DAG); |
| } |
| |
| SDValue CSKYTargetLowering::LowerVASTART(SDValue Op, SelectionDAG &DAG) const { |
| MachineFunction &MF = DAG.getMachineFunction(); |
| CSKYMachineFunctionInfo *FuncInfo = MF.getInfo<CSKYMachineFunctionInfo>(); |
| |
| SDLoc DL(Op); |
| SDValue FI = DAG.getFrameIndex(FuncInfo->getVarArgsFrameIndex(), |
| getPointerTy(MF.getDataLayout())); |
| |
| // vastart just stores the address of the VarArgsFrameIndex slot into the |
| // memory location argument. |
| const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue(); |
| return DAG.getStore(Op.getOperand(0), DL, FI, Op.getOperand(1), |
| MachinePointerInfo(SV)); |
| } |
| |
| SDValue CSKYTargetLowering::LowerFRAMEADDR(SDValue Op, |
| SelectionDAG &DAG) const { |
| const CSKYRegisterInfo &RI = *Subtarget.getRegisterInfo(); |
| MachineFunction &MF = DAG.getMachineFunction(); |
| MachineFrameInfo &MFI = MF.getFrameInfo(); |
| MFI.setFrameAddressIsTaken(true); |
| |
| EVT VT = Op.getValueType(); |
| SDLoc dl(Op); |
| unsigned Depth = Op.getConstantOperandVal(0); |
| Register FrameReg = RI.getFrameRegister(MF); |
| SDValue FrameAddr = DAG.getCopyFromReg(DAG.getEntryNode(), dl, FrameReg, VT); |
| while (Depth--) |
| FrameAddr = DAG.getLoad(VT, dl, DAG.getEntryNode(), FrameAddr, |
| MachinePointerInfo()); |
| return FrameAddr; |
| } |
| |
| SDValue CSKYTargetLowering::LowerRETURNADDR(SDValue Op, |
| SelectionDAG &DAG) const { |
| const CSKYRegisterInfo &RI = *Subtarget.getRegisterInfo(); |
| MachineFunction &MF = DAG.getMachineFunction(); |
| MachineFrameInfo &MFI = MF.getFrameInfo(); |
| MFI.setReturnAddressIsTaken(true); |
| |
| if (verifyReturnAddressArgumentIsConstant(Op, DAG)) |
| return SDValue(); |
| |
| EVT VT = Op.getValueType(); |
| SDLoc dl(Op); |
| unsigned Depth = Op.getConstantOperandVal(0); |
| if (Depth) { |
| SDValue FrameAddr = LowerFRAMEADDR(Op, DAG); |
| SDValue Offset = DAG.getConstant(4, dl, MVT::i32); |
| return DAG.getLoad(VT, dl, DAG.getEntryNode(), |
| DAG.getNode(ISD::ADD, dl, VT, FrameAddr, Offset), |
| MachinePointerInfo()); |
| } |
| // Return the value of the return address register, marking it an implicit |
| // live-in. |
| unsigned Reg = MF.addLiveIn(RI.getRARegister(), getRegClassFor(MVT::i32)); |
| return DAG.getCopyFromReg(DAG.getEntryNode(), dl, Reg, VT); |
| } |
| |
| Register CSKYTargetLowering::getExceptionPointerRegister( |
| const Constant *PersonalityFn) const { |
| return CSKY::R0; |
| } |
| |
| Register CSKYTargetLowering::getExceptionSelectorRegister( |
| const Constant *PersonalityFn) const { |
| return CSKY::R1; |
| } |
| |
| SDValue CSKYTargetLowering::LowerGlobalTLSAddress(SDValue Op, |
| SelectionDAG &DAG) const { |
| SDLoc DL(Op); |
| EVT Ty = Op.getValueType(); |
| GlobalAddressSDNode *N = cast<GlobalAddressSDNode>(Op); |
| int64_t Offset = N->getOffset(); |
| MVT XLenVT = MVT::i32; |
| |
| TLSModel::Model Model = getTargetMachine().getTLSModel(N->getGlobal()); |
| SDValue Addr; |
| switch (Model) { |
| case TLSModel::LocalExec: |
| Addr = getStaticTLSAddr(N, DAG, /*UseGOT=*/false); |
| break; |
| case TLSModel::InitialExec: |
| Addr = getStaticTLSAddr(N, DAG, /*UseGOT=*/true); |
| break; |
| case TLSModel::LocalDynamic: |
| case TLSModel::GeneralDynamic: |
| Addr = getDynamicTLSAddr(N, DAG); |
| break; |
| } |
| |
| // In order to maximise the opportunity for common subexpression elimination, |
| // emit a separate ADD node for the global address offset instead of folding |
| // it in the global address node. Later peephole optimisations may choose to |
| // fold it back in when profitable. |
| if (Offset != 0) |
| return DAG.getNode(ISD::ADD, DL, Ty, Addr, |
| DAG.getConstant(Offset, DL, XLenVT)); |
| return Addr; |
| } |
| |
| SDValue CSKYTargetLowering::getStaticTLSAddr(GlobalAddressSDNode *N, |
| SelectionDAG &DAG, |
| bool UseGOT) const { |
| MachineFunction &MF = DAG.getMachineFunction(); |
| CSKYMachineFunctionInfo *CFI = MF.getInfo<CSKYMachineFunctionInfo>(); |
| |
| unsigned CSKYPCLabelIndex = CFI->createPICLabelUId(); |
| |
| SDLoc DL(N); |
| EVT Ty = getPointerTy(DAG.getDataLayout()); |
| |
| CSKYCP::CSKYCPModifier Flag = UseGOT ? CSKYCP::TLSIE : CSKYCP::TLSLE; |
| bool AddCurrentAddr = UseGOT ? true : false; |
| unsigned char PCAjust = UseGOT ? 4 : 0; |
| |
| CSKYConstantPoolValue *CPV = |
| CSKYConstantPoolConstant::Create(N->getGlobal(), CSKYCP::CPValue, PCAjust, |
| Flag, AddCurrentAddr, CSKYPCLabelIndex); |
| SDValue CAddr = DAG.getTargetConstantPool(CPV, Ty); |
| |
| SDValue Load; |
| if (UseGOT) { |
| SDValue PICLabel = DAG.getTargetConstant(CSKYPCLabelIndex, DL, MVT::i32); |
| auto *LRWGRS = DAG.getMachineNode(CSKY::PseudoTLSLA32, DL, {Ty, Ty}, |
| {CAddr, PICLabel}); |
| auto LRWADDGRS = |
| DAG.getNode(ISD::ADD, DL, Ty, SDValue(LRWGRS, 0), SDValue(LRWGRS, 1)); |
| Load = DAG.getLoad(Ty, DL, DAG.getEntryNode(), LRWADDGRS, |
| MachinePointerInfo(N->getGlobal())); |
| } else { |
| Load = SDValue(DAG.getMachineNode(CSKY::LRW32, DL, Ty, CAddr), 0); |
| } |
| |
| // Add the thread pointer. |
| SDValue TPReg = DAG.getRegister(CSKY::R31, MVT::i32); |
| return DAG.getNode(ISD::ADD, DL, Ty, Load, TPReg); |
| } |
| |
| SDValue CSKYTargetLowering::getDynamicTLSAddr(GlobalAddressSDNode *N, |
| SelectionDAG &DAG) const { |
| MachineFunction &MF = DAG.getMachineFunction(); |
| CSKYMachineFunctionInfo *CFI = MF.getInfo<CSKYMachineFunctionInfo>(); |
| |
| unsigned CSKYPCLabelIndex = CFI->createPICLabelUId(); |
| |
| SDLoc DL(N); |
| EVT Ty = getPointerTy(DAG.getDataLayout()); |
| IntegerType *CallTy = Type::getIntNTy(*DAG.getContext(), Ty.getSizeInBits()); |
| |
| CSKYConstantPoolValue *CPV = |
| CSKYConstantPoolConstant::Create(N->getGlobal(), CSKYCP::CPValue, 4, |
| CSKYCP::TLSGD, true, CSKYPCLabelIndex); |
| SDValue Addr = DAG.getTargetConstantPool(CPV, Ty); |
| SDValue PICLabel = DAG.getTargetConstant(CSKYPCLabelIndex, DL, MVT::i32); |
| |
| auto *LRWGRS = |
| DAG.getMachineNode(CSKY::PseudoTLSLA32, DL, {Ty, Ty}, {Addr, PICLabel}); |
| |
| auto Load = |
| DAG.getNode(ISD::ADD, DL, Ty, SDValue(LRWGRS, 0), SDValue(LRWGRS, 1)); |
| |
| // Prepare argument list to generate call. |
| ArgListTy Args; |
| ArgListEntry Entry; |
| Entry.Node = Load; |
| Entry.Ty = CallTy; |
| Args.push_back(Entry); |
| |
| // Setup call to __tls_get_addr. |
| TargetLowering::CallLoweringInfo CLI(DAG); |
| CLI.setDebugLoc(DL) |
| .setChain(DAG.getEntryNode()) |
| .setLibCallee(CallingConv::C, CallTy, |
| DAG.getExternalSymbol("__tls_get_addr", Ty), |
| std::move(Args)); |
| SDValue V = LowerCallTo(CLI).first; |
| |
| return V; |
| } |
| |
| bool CSKYTargetLowering::decomposeMulByConstant(LLVMContext &Context, EVT VT, |
| SDValue C) const { |
| if (!VT.isScalarInteger()) |
| return false; |
| |
| // Omit if data size exceeds. |
| if (VT.getSizeInBits() > Subtarget.XLen) |
| return false; |
| |
| if (auto *ConstNode = dyn_cast<ConstantSDNode>(C.getNode())) { |
| const APInt &Imm = ConstNode->getAPIntValue(); |
| // Break MULT to LSLI + ADDU/SUBU. |
| if ((Imm + 1).isPowerOf2() || (Imm - 1).isPowerOf2() || |
| (1 - Imm).isPowerOf2()) |
| return true; |
| // Only break MULT for sub targets without MULT32, since an extra |
| // instruction will be generated against the above 3 cases. We leave it |
| // unchanged on sub targets with MULT32, since not sure it is better. |
| if (!Subtarget.hasE2() && (-1 - Imm).isPowerOf2()) |
| return true; |
| // Break (MULT x, imm) to ([IXH32|IXW32|IXD32] (LSLI32 x, i0), x) when |
| // imm=(1<<i0)+[2|4|8] and imm has to be composed via a MOVIH32/ORI32 pair. |
| if (Imm.ugt(0xffff) && ((Imm - 2).isPowerOf2() || (Imm - 4).isPowerOf2()) && |
| Subtarget.hasE2()) |
| return true; |
| if (Imm.ugt(0xffff) && (Imm - 8).isPowerOf2() && Subtarget.has2E3()) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool CSKYTargetLowering::isCheapToSpeculateCttz(Type *Ty) const { |
| return Subtarget.has2E3(); |
| } |
| |
| bool CSKYTargetLowering::isCheapToSpeculateCtlz(Type *Ty) const { |
| return Subtarget.hasE2(); |
| } |