| //===- 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/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); | |
| // 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); | |
| Index = DAG.getNode(ISD::MUL, DL, Index.getValueType(), Index, | |
| DAG.getConstant(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(ConstantPoolSDNode *CP, | |
| SelectionDAG &DAG) const { | |
| EVT PtrVT = getPointerTy(DAG.getDataLayout()); | |
| 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::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::ConstantPool: | |
| return LowerConstantPool(cast<ConstantPoolSDNode>(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); | |
| 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::PCREL_WRAPPER: | |
| return "XtensaISD::PCREL_WRAPPER"; | |
| case XtensaISD::RET: | |
| return "XtensaISD::RET"; | |
| case XtensaISD::SELECT_CC: | |
| return "XtensaISD::SELECT_CC"; | |
| } | |
| 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 { | |
| switch (MI.getOpcode()) { | |
| case Xtensa::SELECT: | |
| return emitSelectCC(MI, MBB); | |
| default: | |
| llvm_unreachable("Unexpected instr type to insert"); | |
| } | |
| } |