| //===- XtensaISelLowering.cpp - Xtensa 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 Xtensa uses to lower LLVM code into a |
| // selection DAG. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "XtensaISelLowering.h" |
| #include "XtensaConstantPoolValue.h" |
| #include "XtensaSubtarget.h" |
| #include "XtensaTargetMachine.h" |
| #include "llvm/CodeGen/CallingConvLower.h" |
| #include "llvm/CodeGen/MachineFrameInfo.h" |
| #include "llvm/CodeGen/MachineInstrBuilder.h" |
| #include "llvm/CodeGen/MachineJumpTableInfo.h" |
| #include "llvm/CodeGen/MachineRegisterInfo.h" |
| #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" |
| #include "llvm/IR/GlobalVariable.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/MathExtras.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <deque> |
| |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "xtensa-lower" |
| |
| // Return true if we must use long (in fact, indirect) function call. |
| // It's simplified version, production implimentation must |
| // resolve a functions in ROM (usually glibc functions) |
| static bool isLongCall(const char *str) { |
| // Currently always use long calls |
| return true; |
| } |
| |
| XtensaTargetLowering::XtensaTargetLowering(const TargetMachine &TM, |
| const XtensaSubtarget &STI) |
| : TargetLowering(TM), Subtarget(STI) { |
| MVT PtrVT = MVT::i32; |
| // Set up the register classes. |
| addRegisterClass(MVT::i32, &Xtensa::ARRegClass); |
| |
| // Set up special registers. |
| setStackPointerRegisterToSaveRestore(Xtensa::SP); |
| |
| setSchedulingPreference(Sched::RegPressure); |
| |
| setMinFunctionAlignment(Align(4)); |
| |
| setOperationAction(ISD::Constant, MVT::i32, Custom); |
| setOperationAction(ISD::Constant, MVT::i64, Expand); |
| |
| setBooleanContents(ZeroOrOneBooleanContent); |
| |
| setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand); |
| setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i8, Expand); |
| setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i16, Expand); |
| |
| setOperationAction(ISD::BITCAST, MVT::i32, Expand); |
| setOperationAction(ISD::BITCAST, MVT::f32, Expand); |
| setOperationAction(ISD::UINT_TO_FP, MVT::i32, Expand); |
| setOperationAction(ISD::SINT_TO_FP, MVT::i32, Expand); |
| setOperationAction(ISD::FP_TO_UINT, MVT::i32, Expand); |
| setOperationAction(ISD::FP_TO_SINT, MVT::i32, Expand); |
| |
| // No sign extend instructions for i1 |
| for (MVT VT : MVT::integer_valuetypes()) { |
| setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i1, Promote); |
| setLoadExtAction(ISD::ZEXTLOAD, VT, MVT::i1, Promote); |
| setLoadExtAction(ISD::EXTLOAD, VT, MVT::i1, Promote); |
| } |
| |
| setOperationAction(ISD::ConstantPool, PtrVT, Custom); |
| setOperationAction(ISD::GlobalAddress, PtrVT, Custom); |
| setOperationAction(ISD::BlockAddress, PtrVT, Custom); |
| setOperationAction(ISD::JumpTable, PtrVT, Custom); |
| |
| // Expand jump table branches as address arithmetic followed by an |
| // indirect jump. |
| setOperationAction(ISD::BR_JT, MVT::Other, Custom); |
| |
| setOperationAction(ISD::BR_CC, MVT::i32, Legal); |
| setOperationAction(ISD::BR_CC, MVT::i64, Expand); |
| setOperationAction(ISD::BR_CC, MVT::f32, Expand); |
| |
| setOperationAction(ISD::SELECT, MVT::i32, Expand); |
| setOperationAction(ISD::SELECT_CC, MVT::i32, Custom); |
| setOperationAction(ISD::SETCC, MVT::i32, Expand); |
| |
| setCondCodeAction(ISD::SETGT, MVT::i32, Expand); |
| setCondCodeAction(ISD::SETLE, MVT::i32, Expand); |
| setCondCodeAction(ISD::SETUGT, MVT::i32, Expand); |
| setCondCodeAction(ISD::SETULE, MVT::i32, Expand); |
| |
| setOperationAction(ISD::MUL, MVT::i32, Expand); |
| setOperationAction(ISD::MULHU, MVT::i32, Expand); |
| setOperationAction(ISD::MULHS, MVT::i32, Expand); |
| setOperationAction(ISD::SMUL_LOHI, MVT::i32, Expand); |
| setOperationAction(ISD::UMUL_LOHI, MVT::i32, Expand); |
| |
| setOperationAction(ISD::SDIV, MVT::i32, Expand); |
| setOperationAction(ISD::UDIV, MVT::i32, Expand); |
| setOperationAction(ISD::SREM, MVT::i32, Expand); |
| setOperationAction(ISD::UREM, MVT::i32, Expand); |
| setOperationAction(ISD::SDIVREM, MVT::i32, Expand); |
| setOperationAction(ISD::UDIVREM, MVT::i32, Expand); |
| |
| setOperationAction(ISD::SHL_PARTS, MVT::i32, Custom); |
| setOperationAction(ISD::SRA_PARTS, MVT::i32, Custom); |
| setOperationAction(ISD::SRL_PARTS, MVT::i32, Custom); |
| |
| setOperationAction(ISD::BSWAP, MVT::i32, Expand); |
| setOperationAction(ISD::ROTL, MVT::i32, Expand); |
| setOperationAction(ISD::ROTR, MVT::i32, Expand); |
| setOperationAction(ISD::CTPOP, MVT::i32, Custom); |
| setOperationAction(ISD::CTTZ, MVT::i32, Expand); |
| setOperationAction(ISD::CTLZ, MVT::i32, Expand); |
| setOperationAction(ISD::CTTZ_ZERO_UNDEF, MVT::i32, Expand); |
| setOperationAction(ISD::CTLZ_ZERO_UNDEF, MVT::i32, Expand); |
| |
| // Implement custom stack allocations |
| setOperationAction(ISD::DYNAMIC_STACKALLOC, PtrVT, Custom); |
| // Implement custom stack save and restore |
| setOperationAction(ISD::STACKSAVE, MVT::Other, Custom); |
| setOperationAction(ISD::STACKRESTORE, MVT::Other, Custom); |
| |
| // Compute derived properties from the register classes |
| computeRegisterProperties(STI.getRegisterInfo()); |
| } |
| |
| bool XtensaTargetLowering::isOffsetFoldingLegal( |
| const GlobalAddressSDNode *GA) const { |
| // The Xtensa target isn't yet aware of offsets. |
| return false; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Calling conventions |
| //===----------------------------------------------------------------------===// |
| |
| #include "XtensaGenCallingConv.inc" |
| |
| static bool CC_Xtensa_Custom(unsigned ValNo, MVT ValVT, MVT LocVT, |
| CCValAssign::LocInfo LocInfo, |
| ISD::ArgFlagsTy ArgFlags, CCState &State) { |
| static const MCPhysReg IntRegs[] = {Xtensa::A2, Xtensa::A3, Xtensa::A4, |
| Xtensa::A5, Xtensa::A6, Xtensa::A7}; |
| |
| if (ArgFlags.isByVal()) { |
| Align ByValAlign = ArgFlags.getNonZeroByValAlign(); |
| unsigned ByValSize = ArgFlags.getByValSize(); |
| if (ByValSize < 4) { |
| ByValSize = 4; |
| } |
| if (ByValAlign < Align(4)) { |
| ByValAlign = Align(4); |
| } |
| unsigned Offset = State.AllocateStack(ByValSize, ByValAlign); |
| State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset, LocVT, LocInfo)); |
| // Mark all unused registers as allocated to avoid misuse |
| // of such registers. |
| while (State.AllocateReg(IntRegs)) |
| ; |
| return false; |
| } |
| |
| // Promote i8 and i16 |
| if (LocVT == MVT::i8 || LocVT == MVT::i16) { |
| LocVT = MVT::i32; |
| if (ArgFlags.isSExt()) |
| LocInfo = CCValAssign::SExt; |
| else if (ArgFlags.isZExt()) |
| LocInfo = CCValAssign::ZExt; |
| else |
| LocInfo = CCValAssign::AExt; |
| } |
| |
| unsigned Register; |
| |
| Align OrigAlign = ArgFlags.getNonZeroOrigAlign(); |
| bool needs64BitAlign = (ValVT == MVT::i32 && OrigAlign == Align(8)); |
| bool needs128BitAlign = (ValVT == MVT::i32 && OrigAlign == Align(16)); |
| |
| if (ValVT == MVT::i32) { |
| Register = State.AllocateReg(IntRegs); |
| // If this is the first part of an i64 arg, |
| // the allocated register must be either A2, A4 or A6. |
| if (needs64BitAlign && (Register == Xtensa::A3 || Register == Xtensa::A5 || |
| Register == Xtensa::A7)) |
| Register = State.AllocateReg(IntRegs); |
| // arguments with 16byte alignment must be passed in the first register or |
| // passed via stack |
| if (needs128BitAlign && (Register != Xtensa::A2)) |
| while ((Register = State.AllocateReg(IntRegs))) |
| ; |
| LocVT = MVT::i32; |
| } else if (ValVT == MVT::f64) { |
| // Allocate int register and shadow next int register. |
| Register = State.AllocateReg(IntRegs); |
| if (Register == Xtensa::A3 || Register == Xtensa::A5 || |
| Register == Xtensa::A7) |
| Register = State.AllocateReg(IntRegs); |
| State.AllocateReg(IntRegs); |
| LocVT = MVT::i32; |
| } else { |
| report_fatal_error("Cannot handle this ValVT."); |
| } |
| |
| if (!Register) { |
| unsigned Offset = State.AllocateStack(ValVT.getStoreSize(), OrigAlign); |
| State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset, LocVT, LocInfo)); |
| } else { |
| State.addLoc(CCValAssign::getReg(ValNo, ValVT, Register, LocVT, LocInfo)); |
| } |
| |
| return false; |
| } |
| |
| CCAssignFn *XtensaTargetLowering::CCAssignFnForCall(CallingConv::ID CC, |
| bool IsVarArg) const { |
| return CC_Xtensa_Custom; |
| } |
| |
| SDValue XtensaTargetLowering::LowerFormalArguments( |
| SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, |
| const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL, |
| SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const { |
| MachineFunction &MF = DAG.getMachineFunction(); |
| MachineFrameInfo &MFI = MF.getFrameInfo(); |
| |
| // Used with vargs to acumulate store chains. |
| std::vector<SDValue> OutChains; |
| |
| if (IsVarArg) |
| report_fatal_error("Var arg not supported by FormalArguments Lowering"); |
| |
| // Assign locations to all of the incoming arguments. |
| SmallVector<CCValAssign, 16> ArgLocs; |
| CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), ArgLocs, |
| *DAG.getContext()); |
| |
| CCInfo.AnalyzeFormalArguments(Ins, CCAssignFnForCall(CallConv, IsVarArg)); |
| |
| for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { |
| CCValAssign &VA = ArgLocs[i]; |
| // Arguments stored on registers |
| if (VA.isRegLoc()) { |
| EVT RegVT = VA.getLocVT(); |
| const TargetRegisterClass *RC; |
| |
| if (RegVT == MVT::i32) |
| RC = &Xtensa::ARRegClass; |
| else |
| report_fatal_error("RegVT not supported by FormalArguments Lowering"); |
| |
| // Transform the arguments stored on |
| // physical registers into virtual ones |
| unsigned Register = MF.addLiveIn(VA.getLocReg(), RC); |
| SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Register, RegVT); |
| |
| // If this is an 8 or 16-bit value, it has been passed promoted |
| // to 32 bits. Insert an assert[sz]ext to capture this, then |
| // truncate to the right size. |
| if (VA.getLocInfo() != CCValAssign::Full) { |
| unsigned Opcode = 0; |
| if (VA.getLocInfo() == CCValAssign::SExt) |
| Opcode = ISD::AssertSext; |
| else if (VA.getLocInfo() == CCValAssign::ZExt) |
| Opcode = ISD::AssertZext; |
| if (Opcode) |
| ArgValue = DAG.getNode(Opcode, DL, RegVT, ArgValue, |
| DAG.getValueType(VA.getValVT())); |
| ArgValue = DAG.getNode((VA.getValVT() == MVT::f32) ? ISD::BITCAST |
| : ISD::TRUNCATE, |
| DL, VA.getValVT(), ArgValue); |
| } |
| |
| InVals.push_back(ArgValue); |
| |
| } else { |
| assert(VA.isMemLoc()); |
| |
| EVT ValVT = VA.getValVT(); |
| |
| // The stack pointer offset is relative to the caller stack frame. |
| int FI = MFI.CreateFixedObject(ValVT.getStoreSize(), VA.getLocMemOffset(), |
| true); |
| |
| if (Ins[VA.getValNo()].Flags.isByVal()) { |
| // Assume that in this case load operation is created |
| SDValue FIN = DAG.getFrameIndex(FI, MVT::i32); |
| InVals.push_back(FIN); |
| } else { |
| // Create load nodes to retrieve arguments from the stack |
| SDValue FIN = |
| DAG.getFrameIndex(FI, getFrameIndexTy(DAG.getDataLayout())); |
| InVals.push_back(DAG.getLoad( |
| ValVT, DL, Chain, FIN, |
| MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI))); |
| } |
| } |
| } |
| |
| // All stores are grouped in one node to allow the matching between |
| // the size of Ins and InVals. This only happens when on varg functions |
| if (!OutChains.empty()) { |
| OutChains.push_back(Chain); |
| Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, OutChains); |
| } |
| |
| return Chain; |
| } |
| |
| SDValue |
| XtensaTargetLowering::LowerCall(CallLoweringInfo &CLI, |
| SmallVectorImpl<SDValue> &InVals) const { |
| SelectionDAG &DAG = CLI.DAG; |
| SDLoc &DL = CLI.DL; |
| SmallVector<ISD::OutputArg, 32> &Outs = CLI.Outs; |
| SmallVector<SDValue, 32> &OutVals = CLI.OutVals; |
| SmallVector<ISD::InputArg, 32> &Ins = CLI.Ins; |
| SDValue Chain = CLI.Chain; |
| SDValue Callee = CLI.Callee; |
| bool &IsTailCall = CLI.IsTailCall; |
| CallingConv::ID CallConv = CLI.CallConv; |
| bool IsVarArg = CLI.IsVarArg; |
| |
| MachineFunction &MF = DAG.getMachineFunction(); |
| EVT PtrVT = getPointerTy(DAG.getDataLayout()); |
| const TargetFrameLowering *TFL = Subtarget.getFrameLowering(); |
| |
| // TODO: Support tail call optimization. |
| IsTailCall = false; |
| |
| // Analyze the operands of the call, assigning locations to each operand. |
| SmallVector<CCValAssign, 16> ArgLocs; |
| CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); |
| |
| CCAssignFn *CC = CCAssignFnForCall(CallConv, IsVarArg); |
| |
| CCInfo.AnalyzeCallOperands(Outs, CC); |
| |
| // Get a count of how many bytes are to be pushed on the stack. |
| unsigned NumBytes = CCInfo.getStackSize(); |
| |
| Align StackAlignment = TFL->getStackAlign(); |
| unsigned NextStackOffset = alignTo(NumBytes, StackAlignment); |
| |
| Chain = DAG.getCALLSEQ_START(Chain, NextStackOffset, 0, DL); |
| |
| // Copy argument values to their designated locations. |
| std::deque<std::pair<unsigned, SDValue>> RegsToPass; |
| SmallVector<SDValue, 8> MemOpChains; |
| SDValue StackPtr; |
| for (unsigned I = 0, E = ArgLocs.size(); I != E; ++I) { |
| CCValAssign &VA = ArgLocs[I]; |
| SDValue ArgValue = OutVals[I]; |
| ISD::ArgFlagsTy Flags = Outs[I].Flags; |
| |
| if (VA.isRegLoc()) |
| // Queue up the argument copies and emit them at the end. |
| RegsToPass.push_back(std::make_pair(VA.getLocReg(), ArgValue)); |
| else if (Flags.isByVal()) { |
| assert(VA.isMemLoc()); |
| assert(Flags.getByValSize() && |
| "ByVal args of size 0 should have been ignored by front-end."); |
| assert(!IsTailCall && |
| "Do not tail-call optimize if there is a byval argument."); |
| |
| if (!StackPtr.getNode()) |
| StackPtr = DAG.getCopyFromReg(Chain, DL, Xtensa::SP, PtrVT); |
| unsigned Offset = VA.getLocMemOffset(); |
| SDValue Address = DAG.getNode(ISD::ADD, DL, PtrVT, StackPtr, |
| DAG.getIntPtrConstant(Offset, DL)); |
| SDValue SizeNode = DAG.getConstant(Flags.getByValSize(), DL, MVT::i32); |
| SDValue Memcpy = DAG.getMemcpy( |
| Chain, DL, Address, ArgValue, SizeNode, Flags.getNonZeroByValAlign(), |
| /*isVolatile=*/false, /*AlwaysInline=*/false, |
| /*CI=*/nullptr, std::nullopt, MachinePointerInfo(), MachinePointerInfo()); |
| MemOpChains.push_back(Memcpy); |
| } else { |
| assert(VA.isMemLoc() && "Argument not register or memory"); |
| |
| // Work out the address of the stack slot. Unpromoted ints and |
| // floats are passed as right-justified 8-byte values. |
| if (!StackPtr.getNode()) |
| StackPtr = DAG.getCopyFromReg(Chain, DL, Xtensa::SP, PtrVT); |
| unsigned Offset = VA.getLocMemOffset(); |
| SDValue Address = DAG.getNode(ISD::ADD, DL, PtrVT, StackPtr, |
| DAG.getIntPtrConstant(Offset, 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); |
| |
| // Build a sequence of copy-to-reg nodes, chained and glued together. |
| SDValue Glue; |
| for (unsigned I = 0, E = RegsToPass.size(); I != E; ++I) { |
| unsigned Reg = RegsToPass[I].first; |
| Chain = DAG.getCopyToReg(Chain, DL, Reg, RegsToPass[I].second, Glue); |
| Glue = Chain.getValue(1); |
| } |
| std::string name; |
| unsigned char TF = 0; |
| |
| // Accept direct calls by converting symbolic call addresses to the |
| // associated Target* opcodes. |
| if (ExternalSymbolSDNode *E = dyn_cast<ExternalSymbolSDNode>(Callee)) { |
| name = E->getSymbol(); |
| TF = E->getTargetFlags(); |
| if (isPositionIndependent()) { |
| report_fatal_error("PIC relocations is not supported"); |
| } else |
| Callee = DAG.getTargetExternalSymbol(E->getSymbol(), PtrVT, TF); |
| } else if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { |
| const GlobalValue *GV = G->getGlobal(); |
| name = GV->getName().str(); |
| } |
| |
| if ((!name.empty()) && isLongCall(name.c_str())) { |
| // Create a constant pool entry for the callee address |
| XtensaCP::XtensaCPModifier Modifier = XtensaCP::no_modifier; |
| |
| XtensaConstantPoolValue *CPV = XtensaConstantPoolSymbol::Create( |
| *DAG.getContext(), name.c_str(), 0 /* XtensaCLabelIndex */, false, |
| Modifier); |
| |
| // Get the address of the callee into a register |
| SDValue CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, Align(4), 0, TF); |
| SDValue CPWrap = getAddrPCRel(CPAddr, DAG); |
| Callee = CPWrap; |
| } |
| |
| // The first call operand is the chain and the second is the target address. |
| SmallVector<SDValue, 8> Ops; |
| Ops.push_back(Chain); |
| Ops.push_back(Callee); |
| |
| // 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)); |
| |
| // Add argument registers to the end of the list so that they are |
| // known live into the call. |
| for (unsigned I = 0, E = RegsToPass.size(); I != E; ++I) { |
| unsigned Reg = RegsToPass[I].first; |
| Ops.push_back(DAG.getRegister(Reg, RegsToPass[I].second.getValueType())); |
| } |
| |
| // Glue the call to the argument copies, if any. |
| if (Glue.getNode()) |
| Ops.push_back(Glue); |
| |
| SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); |
| Chain = DAG.getNode(XtensaISD::CALL, DL, NodeTys, Ops); |
| Glue = Chain.getValue(1); |
| |
| // Mark the end of the call, which is glued to the call itself. |
| Chain = DAG.getCALLSEQ_END(Chain, DAG.getConstant(NumBytes, DL, PtrVT, true), |
| DAG.getConstant(0, DL, PtrVT, true), Glue, DL); |
| Glue = Chain.getValue(1); |
| |
| // Assign locations to each value returned by this call. |
| SmallVector<CCValAssign, 16> RetLocs; |
| CCState RetCCInfo(CallConv, IsVarArg, MF, RetLocs, *DAG.getContext()); |
| RetCCInfo.AnalyzeCallResult(Ins, RetCC_Xtensa); |
| |
| // Copy all of the result registers out of their specified physreg. |
| for (unsigned I = 0, E = RetLocs.size(); I != E; ++I) { |
| CCValAssign &VA = RetLocs[I]; |
| |
| // Copy the value out, gluing the copy to the end of the call sequence. |
| unsigned Reg = VA.getLocReg(); |
| SDValue RetValue = DAG.getCopyFromReg(Chain, DL, Reg, VA.getLocVT(), Glue); |
| Chain = RetValue.getValue(1); |
| Glue = RetValue.getValue(2); |
| |
| InVals.push_back(RetValue); |
| } |
| return Chain; |
| } |
| |
| bool XtensaTargetLowering::CanLowerReturn( |
| CallingConv::ID CallConv, MachineFunction &MF, bool IsVarArg, |
| const SmallVectorImpl<ISD::OutputArg> &Outs, LLVMContext &Context) const { |
| SmallVector<CCValAssign, 16> RVLocs; |
| CCState CCInfo(CallConv, IsVarArg, MF, RVLocs, Context); |
| return CCInfo.CheckReturn(Outs, RetCC_Xtensa); |
| } |
| |
| SDValue |
| XtensaTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, |
| bool IsVarArg, |
| const SmallVectorImpl<ISD::OutputArg> &Outs, |
| const SmallVectorImpl<SDValue> &OutVals, |
| const SDLoc &DL, SelectionDAG &DAG) const { |
| if (IsVarArg) |
| report_fatal_error("VarArg not supported"); |
| |
| MachineFunction &MF = DAG.getMachineFunction(); |
| |
| // Assign locations to each returned value. |
| SmallVector<CCValAssign, 16> RetLocs; |
| CCState RetCCInfo(CallConv, IsVarArg, MF, RetLocs, *DAG.getContext()); |
| RetCCInfo.AnalyzeReturn(Outs, RetCC_Xtensa); |
| |
| SDValue Glue; |
| // Quick exit for void returns |
| if (RetLocs.empty()) |
| return DAG.getNode(XtensaISD::RET, DL, MVT::Other, Chain); |
| |
| // Copy the result values into the output registers. |
| SmallVector<SDValue, 4> RetOps; |
| RetOps.push_back(Chain); |
| for (unsigned I = 0, E = RetLocs.size(); I != E; ++I) { |
| CCValAssign &VA = RetLocs[I]; |
| SDValue RetValue = OutVals[I]; |
| |
| // Make the return register live on exit. |
| assert(VA.isRegLoc() && "Can only return in registers!"); |
| |
| // Chain and glue the copies together. |
| unsigned Register = VA.getLocReg(); |
| Chain = DAG.getCopyToReg(Chain, DL, Register, RetValue, Glue); |
| Glue = Chain.getValue(1); |
| RetOps.push_back(DAG.getRegister(Register, VA.getLocVT())); |
| } |
| |
| // Update chain and glue. |
| RetOps[0] = Chain; |
| if (Glue.getNode()) |
| RetOps.push_back(Glue); |
| |
| return DAG.getNode(XtensaISD::RET, DL, MVT::Other, RetOps); |
| } |
| |
| static unsigned getBranchOpcode(ISD::CondCode Cond) { |
| switch (Cond) { |
| case ISD::SETEQ: |
| return Xtensa::BEQ; |
| case ISD::SETNE: |
| return Xtensa::BNE; |
| case ISD::SETLT: |
| return Xtensa::BLT; |
| case ISD::SETLE: |
| return Xtensa::BGE; |
| case ISD::SETGT: |
| return Xtensa::BLT; |
| case ISD::SETGE: |
| return Xtensa::BGE; |
| case ISD::SETULT: |
| return Xtensa::BLTU; |
| case ISD::SETULE: |
| return Xtensa::BGEU; |
| case ISD::SETUGT: |
| return Xtensa::BLTU; |
| case ISD::SETUGE: |
| return Xtensa::BGEU; |
| default: |
| llvm_unreachable("Unknown branch kind"); |
| } |
| } |
| |
| SDValue XtensaTargetLowering::LowerSELECT_CC(SDValue Op, |
| SelectionDAG &DAG) const { |
| SDLoc DL(Op); |
| EVT Ty = Op.getOperand(0).getValueType(); |
| SDValue LHS = Op.getOperand(0); |
| SDValue RHS = Op.getOperand(1); |
| SDValue TrueValue = Op.getOperand(2); |
| SDValue FalseValue = Op.getOperand(3); |
| ISD::CondCode CC = cast<CondCodeSDNode>(Op->getOperand(4))->get(); |
| |
| unsigned BrOpcode = getBranchOpcode(CC); |
| SDValue TargetCC = DAG.getConstant(BrOpcode, DL, MVT::i32); |
| |
| return DAG.getNode(XtensaISD::SELECT_CC, DL, Ty, LHS, RHS, TrueValue, |
| FalseValue, TargetCC); |
| } |
| |
| SDValue XtensaTargetLowering::LowerImmediate(SDValue Op, |
| SelectionDAG &DAG) const { |
| const ConstantSDNode *CN = cast<ConstantSDNode>(Op); |
| SDLoc DL(CN); |
| APInt APVal = CN->getAPIntValue(); |
| int64_t Value = APVal.getSExtValue(); |
| if (Op.getValueType() == MVT::i32) { |
| // Check if use node maybe lowered to the MOVI instruction |
| if (Value > -2048 && Value <= 2047) |
| return Op; |
| // Check if use node maybe lowered to the ADDMI instruction |
| SDNode &OpNode = *Op.getNode(); |
| if ((OpNode.hasOneUse() && OpNode.use_begin()->getOpcode() == ISD::ADD) && |
| isShiftedInt<16, 8>(Value)) |
| return Op; |
| Type *Ty = Type::getInt32Ty(*DAG.getContext()); |
| Constant *CV = ConstantInt::get(Ty, Value); |
| SDValue CP = DAG.getConstantPool(CV, MVT::i32); |
| return CP; |
| } |
| return Op; |
| } |
| |
| SDValue XtensaTargetLowering::LowerGlobalAddress(SDValue Op, |
| SelectionDAG &DAG) const { |
| const GlobalAddressSDNode *G = cast<GlobalAddressSDNode>(Op); |
| SDLoc DL(Op); |
| auto PtrVT = Op.getValueType(); |
| const GlobalValue *GV = G->getGlobal(); |
| |
| SDValue CPAddr = DAG.getTargetConstantPool(GV, PtrVT, Align(4)); |
| SDValue CPWrap = getAddrPCRel(CPAddr, DAG); |
| |
| return CPWrap; |
| } |
| |
| SDValue XtensaTargetLowering::LowerBlockAddress(SDValue Op, |
| SelectionDAG &DAG) const { |
| BlockAddressSDNode *Node = cast<BlockAddressSDNode>(Op); |
| const BlockAddress *BA = Node->getBlockAddress(); |
| EVT PtrVT = Op.getValueType(); |
| |
| XtensaConstantPoolValue *CPV = |
| XtensaConstantPoolConstant::Create(BA, 0, XtensaCP::CPBlockAddress); |
| SDValue CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, Align(4)); |
| SDValue CPWrap = getAddrPCRel(CPAddr, DAG); |
| |
| return CPWrap; |
| } |
| |
| SDValue XtensaTargetLowering::LowerBR_JT(SDValue Op, SelectionDAG &DAG) const { |
| SDValue Chain = Op.getOperand(0); |
| SDValue Table = Op.getOperand(1); |
| SDValue Index = Op.getOperand(2); |
| SDLoc DL(Op); |
| JumpTableSDNode *JT = cast<JumpTableSDNode>(Table); |
| MachineFunction &MF = DAG.getMachineFunction(); |
| const MachineJumpTableInfo *MJTI = MF.getJumpTableInfo(); |
| SDValue TargetJT = DAG.getTargetJumpTable(JT->getIndex(), MVT::i32); |
| const DataLayout &TD = DAG.getDataLayout(); |
| EVT PtrVT = Table.getValueType(); |
| unsigned EntrySize = MJTI->getEntrySize(TD); |
| |
| assert((MJTI->getEntrySize(TD) == 4) && "Unsupported jump-table entry size"); |
| |
| Index = DAG.getNode( |
| ISD::SHL, DL, Index.getValueType(), Index, |
| DAG.getConstant(Log2_32(EntrySize), DL, Index.getValueType())); |
| |
| SDValue Addr = DAG.getNode(ISD::ADD, DL, Index.getValueType(), Index, Table); |
| SDValue LD = |
| DAG.getLoad(PtrVT, DL, Chain, Addr, |
| MachinePointerInfo::getJumpTable(DAG.getMachineFunction())); |
| |
| return DAG.getNode(XtensaISD::BR_JT, DL, MVT::Other, LD.getValue(1), LD, |
| TargetJT); |
| } |
| |
| SDValue XtensaTargetLowering::LowerJumpTable(SDValue Op, |
| SelectionDAG &DAG) const { |
| JumpTableSDNode *JT = cast<JumpTableSDNode>(Op); |
| EVT PtrVT = Op.getValueType(); |
| |
| // Create a constant pool entry for the callee address |
| XtensaConstantPoolValue *CPV = |
| XtensaConstantPoolJumpTable::Create(*DAG.getContext(), JT->getIndex()); |
| |
| // Get the address of the callee into a register |
| SDValue CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, Align(4)); |
| |
| return getAddrPCRel(CPAddr, DAG); |
| } |
| |
| SDValue XtensaTargetLowering::getAddrPCRel(SDValue Op, |
| SelectionDAG &DAG) const { |
| SDLoc DL(Op); |
| EVT Ty = Op.getValueType(); |
| return DAG.getNode(XtensaISD::PCREL_WRAPPER, DL, Ty, Op); |
| } |
| |
| SDValue XtensaTargetLowering::LowerConstantPool(SDValue Op, |
| SelectionDAG &DAG) const { |
| EVT PtrVT = Op.getValueType(); |
| ConstantPoolSDNode *CP = cast<ConstantPoolSDNode>(Op); |
| SDValue Result; |
| |
| if (!CP->isMachineConstantPoolEntry()) { |
| Result = DAG.getTargetConstantPool(CP->getConstVal(), PtrVT, CP->getAlign(), |
| CP->getOffset()); |
| } else { |
| report_fatal_error("This constantpool type is not supported yet"); |
| } |
| |
| return getAddrPCRel(Result, DAG); |
| } |
| |
| SDValue XtensaTargetLowering::LowerSTACKSAVE(SDValue Op, |
| SelectionDAG &DAG) const { |
| return DAG.getCopyFromReg(Op.getOperand(0), SDLoc(Op), Xtensa::SP, |
| Op.getValueType()); |
| } |
| |
| SDValue XtensaTargetLowering::LowerSTACKRESTORE(SDValue Op, |
| SelectionDAG &DAG) const { |
| return DAG.getCopyToReg(Op.getOperand(0), SDLoc(Op), Xtensa::SP, |
| Op.getOperand(1)); |
| } |
| |
| SDValue XtensaTargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op, |
| SelectionDAG &DAG) const { |
| SDValue Chain = Op.getOperand(0); // Legalize the chain. |
| SDValue Size = Op.getOperand(1); // Legalize the size. |
| EVT VT = Size->getValueType(0); |
| SDLoc DL(Op); |
| |
| // Round up Size to 32 |
| SDValue SizeTmp = |
| DAG.getNode(ISD::ADD, DL, VT, Size, DAG.getConstant(31, DL, MVT::i32)); |
| SDValue SizeRoundUp = DAG.getNode(ISD::AND, DL, VT, SizeTmp, |
| DAG.getConstant(~31, DL, MVT::i32)); |
| |
| unsigned SPReg = Xtensa::SP; |
| SDValue SP = DAG.getCopyFromReg(Chain, DL, SPReg, VT); |
| SDValue NewSP = DAG.getNode(ISD::SUB, DL, VT, SP, SizeRoundUp); // Value |
| Chain = DAG.getCopyToReg(SP.getValue(1), DL, SPReg, NewSP); // Output chain |
| |
| SDValue NewVal = DAG.getCopyFromReg(Chain, DL, SPReg, MVT::i32); |
| Chain = NewVal.getValue(1); |
| |
| SDValue Ops[2] = {NewVal, Chain}; |
| return DAG.getMergeValues(Ops, DL); |
| } |
| |
| SDValue XtensaTargetLowering::LowerShiftLeftParts(SDValue Op, |
| SelectionDAG &DAG) const { |
| SDLoc DL(Op); |
| MVT VT = MVT::i32; |
| SDValue Lo = Op.getOperand(0), Hi = Op.getOperand(1); |
| SDValue Shamt = Op.getOperand(2); |
| |
| // if Shamt - register size < 0: // Shamt < register size |
| // Lo = Lo << Shamt |
| // Hi = (Hi << Shamt) | (Lo >>u (register size - Shamt)) |
| // else: |
| // Lo = 0 |
| // Hi = Lo << (Shamt - register size) |
| |
| SDValue MinusRegisterSize = DAG.getConstant(-32, DL, VT); |
| SDValue ShamtMinusRegisterSize = |
| DAG.getNode(ISD::ADD, DL, VT, Shamt, MinusRegisterSize); |
| |
| SDValue LoTrue = DAG.getNode(ISD::SHL, DL, VT, Lo, Shamt); |
| SDValue HiTrue = DAG.getNode(XtensaISD::SRCL, DL, VT, Hi, Lo, Shamt); |
| SDValue Zero = DAG.getConstant(0, DL, VT); |
| SDValue HiFalse = DAG.getNode(ISD::SHL, DL, VT, Lo, ShamtMinusRegisterSize); |
| |
| SDValue Cond = DAG.getSetCC(DL, VT, ShamtMinusRegisterSize, Zero, ISD::SETLT); |
| Lo = DAG.getNode(ISD::SELECT, DL, VT, Cond, LoTrue, Zero); |
| Hi = DAG.getNode(ISD::SELECT, DL, VT, Cond, HiTrue, HiFalse); |
| |
| return DAG.getMergeValues({Lo, Hi}, DL); |
| } |
| |
| SDValue XtensaTargetLowering::LowerShiftRightParts(SDValue Op, |
| SelectionDAG &DAG, |
| bool IsSRA) const { |
| SDLoc DL(Op); |
| SDValue Lo = Op.getOperand(0), Hi = Op.getOperand(1); |
| SDValue Shamt = Op.getOperand(2); |
| MVT VT = MVT::i32; |
| |
| // SRA expansion: |
| // if Shamt - register size < 0: // Shamt < register size |
| // Lo = (Lo >>u Shamt) | (Hi << u (register size - Shamt)) |
| // Hi = Hi >>s Shamt |
| // else: |
| // Lo = Hi >>s (Shamt - register size); |
| // Hi = Hi >>s (register size - 1) |
| // |
| // SRL expansion: |
| // if Shamt - register size < 0: // Shamt < register size |
| // Lo = (Lo >>u Shamt) | (Hi << u (register size - Shamt)) |
| // Hi = Hi >>u Shamt |
| // else: |
| // Lo = Hi >>u (Shamt - register size); |
| // Hi = 0; |
| |
| unsigned ShiftRightOp = IsSRA ? ISD::SRA : ISD::SRL; |
| SDValue MinusRegisterSize = DAG.getConstant(-32, DL, VT); |
| SDValue RegisterSizeMinus1 = DAG.getConstant(32 - 1, DL, VT); |
| SDValue ShamtMinusRegisterSize = |
| DAG.getNode(ISD::ADD, DL, VT, Shamt, MinusRegisterSize); |
| |
| SDValue LoTrue = DAG.getNode(XtensaISD::SRCR, DL, VT, Hi, Lo, Shamt); |
| SDValue HiTrue = DAG.getNode(ShiftRightOp, DL, VT, Hi, Shamt); |
| SDValue Zero = DAG.getConstant(0, DL, VT); |
| SDValue LoFalse = |
| DAG.getNode(ShiftRightOp, DL, VT, Hi, ShamtMinusRegisterSize); |
| SDValue HiFalse; |
| |
| if (IsSRA) { |
| HiFalse = DAG.getNode(ShiftRightOp, DL, VT, Hi, RegisterSizeMinus1); |
| } else { |
| HiFalse = Zero; |
| } |
| |
| SDValue Cond = DAG.getSetCC(DL, VT, ShamtMinusRegisterSize, Zero, ISD::SETLT); |
| Lo = DAG.getNode(ISD::SELECT, DL, VT, Cond, LoTrue, LoFalse); |
| Hi = DAG.getNode(ISD::SELECT, DL, VT, Cond, HiTrue, HiFalse); |
| |
| return DAG.getMergeValues({Lo, Hi}, DL); |
| } |
| |
| SDValue XtensaTargetLowering::LowerCTPOP(SDValue Op, SelectionDAG &DAG) const { |
| auto &TLI = DAG.getTargetLoweringInfo(); |
| return TLI.expandCTPOP(Op.getNode(), DAG); |
| } |
| |
| bool XtensaTargetLowering::decomposeMulByConstant(LLVMContext &Context, EVT VT, |
| SDValue C) const { |
| APInt Imm; |
| unsigned EltSizeInBits; |
| |
| if (ISD::isConstantSplatVector(C.getNode(), Imm)) { |
| EltSizeInBits = VT.getScalarSizeInBits(); |
| } else if (VT.isScalarInteger()) { |
| EltSizeInBits = VT.getSizeInBits(); |
| if (auto *ConstNode = dyn_cast<ConstantSDNode>(C.getNode())) |
| Imm = ConstNode->getAPIntValue(); |
| else |
| return false; |
| } else { |
| return false; |
| } |
| |
| // Omit if data size exceeds. |
| if (EltSizeInBits > 32) |
| return false; |
| |
| // Convert MULT to LSL. |
| if (Imm.isPowerOf2() && Imm.isIntN(5)) |
| return true; |
| |
| return false; |
| } |
| |
| SDValue XtensaTargetLowering::LowerOperation(SDValue Op, |
| SelectionDAG &DAG) const { |
| switch (Op.getOpcode()) { |
| case ISD::BR_JT: |
| return LowerBR_JT(Op, DAG); |
| case ISD::Constant: |
| return LowerImmediate(Op, DAG); |
| case ISD::GlobalAddress: |
| return LowerGlobalAddress(Op, DAG); |
| case ISD::BlockAddress: |
| return LowerBlockAddress(Op, DAG); |
| case ISD::JumpTable: |
| return LowerJumpTable(Op, DAG); |
| case ISD::CTPOP: |
| return LowerCTPOP(Op, DAG); |
| case ISD::ConstantPool: |
| return LowerConstantPool(Op, DAG); |
| case ISD::SELECT_CC: |
| return LowerSELECT_CC(Op, DAG); |
| case ISD::STACKSAVE: |
| return LowerSTACKSAVE(Op, DAG); |
| case ISD::STACKRESTORE: |
| return LowerSTACKRESTORE(Op, DAG); |
| case ISD::DYNAMIC_STACKALLOC: |
| return LowerDYNAMIC_STACKALLOC(Op, DAG); |
| case ISD::SHL_PARTS: |
| return LowerShiftLeftParts(Op, DAG); |
| case ISD::SRA_PARTS: |
| return LowerShiftRightParts(Op, DAG, true); |
| case ISD::SRL_PARTS: |
| return LowerShiftRightParts(Op, DAG, false); |
| default: |
| report_fatal_error("Unexpected node to lower"); |
| } |
| } |
| |
| const char *XtensaTargetLowering::getTargetNodeName(unsigned Opcode) const { |
| switch (Opcode) { |
| case XtensaISD::BR_JT: |
| return "XtensaISD::BR_JT"; |
| case XtensaISD::CALL: |
| return "XtensaISD::CALL"; |
| case XtensaISD::EXTUI: |
| return "XtensaISD::EXTUI"; |
| case XtensaISD::PCREL_WRAPPER: |
| return "XtensaISD::PCREL_WRAPPER"; |
| case XtensaISD::RET: |
| return "XtensaISD::RET"; |
| case XtensaISD::SELECT_CC: |
| return "XtensaISD::SELECT_CC"; |
| case XtensaISD::SRCL: |
| return "XtensaISD::SRCL"; |
| case XtensaISD::SRCR: |
| return "XtensaISD::SRCR"; |
| } |
| return nullptr; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Custom insertion |
| //===----------------------------------------------------------------------===// |
| |
| MachineBasicBlock * |
| XtensaTargetLowering::emitSelectCC(MachineInstr &MI, |
| MachineBasicBlock *MBB) const { |
| const TargetInstrInfo &TII = *Subtarget.getInstrInfo(); |
| DebugLoc DL = MI.getDebugLoc(); |
| |
| MachineOperand &LHS = MI.getOperand(1); |
| MachineOperand &RHS = MI.getOperand(2); |
| MachineOperand &TrueValue = MI.getOperand(3); |
| MachineOperand &FalseValue = MI.getOperand(4); |
| unsigned BrKind = MI.getOperand(5).getImm(); |
| |
| // To "insert" a SELECT_CC instruction, we actually have to insert |
| // CopyMBB and SinkMBB blocks and add branch to MBB. We build phi |
| // operation in SinkMBB like phi (TrueVakue,FalseValue), where TrueValue |
| // is passed from MMB and FalseValue is passed from CopyMBB. |
| // MBB |
| // | \ |
| // | CopyMBB |
| // | / |
| // SinkMBB |
| // 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 = MBB->getBasicBlock(); |
| MachineFunction::iterator It = ++MBB->getIterator(); |
| |
| MachineFunction *F = MBB->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 MBB and its successor edges to SinkMBB. |
| SinkMBB->splice(SinkMBB->begin(), MBB, |
| std::next(MachineBasicBlock::iterator(MI)), MBB->end()); |
| SinkMBB->transferSuccessorsAndUpdatePHIs(MBB); |
| |
| MBB->addSuccessor(CopyMBB); |
| MBB->addSuccessor(SinkMBB); |
| |
| BuildMI(MBB, DL, TII.get(BrKind)) |
| .addReg(LHS.getReg()) |
| .addReg(RHS.getReg()) |
| .addMBB(SinkMBB); |
| |
| CopyMBB->addSuccessor(SinkMBB); |
| |
| // SinkMBB: |
| // %Result = phi [ %FalseValue, CopyMBB ], [ %TrueValue, MBB ] |
| // ... |
| |
| BuildMI(*SinkMBB, SinkMBB->begin(), DL, TII.get(Xtensa::PHI), |
| MI.getOperand(0).getReg()) |
| .addReg(FalseValue.getReg()) |
| .addMBB(CopyMBB) |
| .addReg(TrueValue.getReg()) |
| .addMBB(MBB); |
| |
| MI.eraseFromParent(); // The pseudo instruction is gone now. |
| return SinkMBB; |
| } |
| |
| MachineBasicBlock *XtensaTargetLowering::EmitInstrWithCustomInserter( |
| MachineInstr &MI, MachineBasicBlock *MBB) const { |
| DebugLoc DL = MI.getDebugLoc(); |
| |
| switch (MI.getOpcode()) { |
| case Xtensa::SELECT: |
| return emitSelectCC(MI, MBB); |
| default: |
| llvm_unreachable("Unexpected instr type to insert"); |
| } |
| } |