//===- 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 "XtensaInstrInfo.h" #include "XtensaMachineFunctionInfo.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 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 and sign extend load i8 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); setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i8, Expand); } 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); // VASTART, VAARG and VACOPY need to deal with the Xtensa-specific varargs // structure, but VAEND is a no-op. setOperationAction(ISD::VASTART, MVT::Other, Custom); setOperationAction(ISD::VAARG, MVT::Other, Custom); setOperationAction(ISD::VACOPY, MVT::Other, Custom); setOperationAction(ISD::VAEND, MVT::Other, Expand); // 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; } //===----------------------------------------------------------------------===// // Inline asm support //===----------------------------------------------------------------------===// TargetLowering::ConstraintType XtensaTargetLowering::getConstraintType(StringRef Constraint) const { if (Constraint.size() == 1) { switch (Constraint[0]) { case 'r': return C_RegisterClass; default: break; } } return TargetLowering::getConstraintType(Constraint); } TargetLowering::ConstraintWeight XtensaTargetLowering::getSingleConstraintMatchWeight( AsmOperandInfo &Info, const char *Constraint) const { ConstraintWeight Weight = CW_Invalid; Value *CallOperandVal = Info.CallOperandVal; // If we don't have a value, we can't do a match, // but allow it at the lowest weight. if (!CallOperandVal) return CW_Default; Type *Ty = CallOperandVal->getType(); // Look at the constraint type. switch (*Constraint) { default: Weight = TargetLowering::getSingleConstraintMatchWeight(Info, Constraint); break; case 'r': if (Ty->isIntegerTy()) Weight = CW_Register; break; } return Weight; } std::pair XtensaTargetLowering::getRegForInlineAsmConstraint( const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const { if (Constraint.size() == 1) { // GCC Constraint Letters switch (Constraint[0]) { default: break; case 'r': // General-purpose register return std::make_pair(0U, &Xtensa::ARRegClass); } } return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); } void XtensaTargetLowering::LowerAsmOperandForConstraint( SDValue Op, StringRef Constraint, std::vector &Ops, SelectionDAG &DAG) const { SDLoc DL(Op); // Only support length 1 constraints for now. if (Constraint.size() > 1) return; TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, Ops, DAG); } //===----------------------------------------------------------------------===// // Calling conventions //===----------------------------------------------------------------------===// #include "XtensaGenCallingConv.inc" static const MCPhysReg IntRegs[] = {Xtensa::A2, Xtensa::A3, Xtensa::A4, Xtensa::A5, Xtensa::A6, Xtensa::A7}; static bool CC_Xtensa_Custom(unsigned ValNo, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, CCState &State) { 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 &Ins, const SDLoc &DL, SelectionDAG &DAG, SmallVectorImpl &InVals) const { MachineFunction &MF = DAG.getMachineFunction(); MachineFrameInfo &MFI = MF.getFrameInfo(); XtensaMachineFunctionInfo *XtensaFI = MF.getInfo(); // Used with vargs to acumulate store chains. std::vector OutChains; // Assign locations to all of the incoming arguments. SmallVector 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(); if (RegVT != MVT::i32) report_fatal_error("RegVT not supported by FormalArguments Lowering"); // Transform the arguments stored on // physical registers into virtual ones Register Reg = MF.addLiveIn(VA.getLocReg(), &Xtensa::ARRegClass); SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, 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))); } } } if (IsVarArg) { unsigned Idx = CCInfo.getFirstUnallocated(IntRegs); unsigned ArgRegsNum = std::size(IntRegs); const TargetRegisterClass *RC = &Xtensa::ARRegClass; MachineFrameInfo &MFI = MF.getFrameInfo(); MachineRegisterInfo &RegInfo = MF.getRegInfo(); unsigned RegSize = 4; MVT RegTy = MVT::i32; MVT FITy = getFrameIndexTy(DAG.getDataLayout()); XtensaFI->setVarArgsFirstGPR(Idx + 2); // 2 - number of a2 register XtensaFI->setVarArgsOnStackFrameIndex( MFI.CreateFixedObject(4, CCInfo.getStackSize(), true)); // 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-a7. 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 (ArgRegsNum == Idx) { VaArgOffset = CCInfo.getStackSize(); VarArgsSaveSize = 0; } else { VarArgsSaveSize = RegSize * (ArgRegsNum - Idx); VaArgOffset = -VarArgsSaveSize; // Record the frame index of the first variable argument // which is a value necessary to VASTART. int FI = MFI.CreateFixedObject(RegSize, VaArgOffset, true); XtensaFI->setVarArgsInRegsFrameIndex(FI); // Copy the integer registers that may have been used for passing varargs // to the vararg save area. for (unsigned I = Idx; I < ArgRegsNum; ++I, VaArgOffset += RegSize) { const Register Reg = RegInfo.createVirtualRegister(RC); RegInfo.addLiveIn(IntRegs[I], Reg); SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, RegTy); FI = MFI.CreateFixedObject(RegSize, VaArgOffset, true); SDValue PtrOff = DAG.getFrameIndex(FI, FITy); SDValue Store = DAG.getStore(Chain, DL, ArgValue, PtrOff, MachinePointerInfo::getFixedStack(MF, FI)); OutChains.push_back(Store); } } } // 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 &InVals) const { SelectionDAG &DAG = CLI.DAG; SDLoc &DL = CLI.DL; SmallVector &Outs = CLI.Outs; SmallVector &OutVals = CLI.OutVals; SmallVector &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 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> RegsToPass; SmallVector 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(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(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 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 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 &Outs, LLVMContext &Context, const Type *RetTy) const { SmallVector 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 &Outs, const SmallVectorImpl &OutVals, const SDLoc &DL, SelectionDAG &DAG) const { MachineFunction &MF = DAG.getMachineFunction(); // Assign locations to each returned value. SmallVector 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 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(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::LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const { // This nodes represent llvm.returnaddress on the DAG. // It takes one operand, the index of the return address to return. // An index of zero corresponds to the current function's return address. // An index of one to the parent's return address, and so on. // Depths > 0 not supported yet! if (Op.getConstantOperandVal(0) != 0) return SDValue(); MachineFunction &MF = DAG.getMachineFunction(); MachineFrameInfo &MFI = MF.getFrameInfo(); EVT VT = Op.getValueType(); MFI.setReturnAddressIsTaken(true); // Return RA, which contains the return address. Mark it an implicit // live-in. Register RA = MF.addLiveIn(Xtensa::A0, getRegClassFor(MVT::i32)); return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), RA, VT); } SDValue XtensaTargetLowering::LowerImmediate(SDValue Op, SelectionDAG &DAG) const { const ConstantSDNode *CN = cast(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.user_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(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(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(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(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(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::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const { // This nodes represent llvm.frameaddress on the DAG. // It takes one operand, the index of the frame address to return. // An index of zero corresponds to the current function's frame address. // An index of one to the parent's frame address, and so on. // Depths > 0 not supported yet! if (Op.getConstantOperandVal(0) != 0) return SDValue(); MachineFunction &MF = DAG.getMachineFunction(); MachineFrameInfo &MFI = MF.getFrameInfo(); MFI.setFrameAddressIsTaken(true); EVT VT = Op.getValueType(); SDLoc DL(Op); Register FrameRegister = Subtarget.getRegisterInfo()->getFrameRegister(MF); SDValue FrameAddr = DAG.getCopyFromReg(DAG.getEntryNode(), DL, FrameRegister, VT); return FrameAddr; } 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.getSignedConstant(~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::LowerVASTART(SDValue Op, SelectionDAG &DAG) const { MachineFunction &MF = DAG.getMachineFunction(); XtensaMachineFunctionInfo *XtensaFI = MF.getInfo(); SDValue Chain = Op.getOperand(0); SDValue Addr = Op.getOperand(1); EVT PtrVT = Addr.getValueType(); SDLoc DL(Op); // Struct va_list_tag // int32 *va_stk - points to the arguments passed in memory // int32 *va_reg - points to the registers with arguments saved in memory // int32 va_ndx - offset from va_stk or va_reg pointers which points to the // next variable argument SDValue VAIndex; SDValue StackOffsetFI = DAG.getFrameIndex(XtensaFI->getVarArgsOnStackFrameIndex(), PtrVT); unsigned ArgWords = XtensaFI->getVarArgsFirstGPR() - 2; // If first variable argument passed in registers (maximum words in registers // is 6) then set va_ndx to the position of this argument in registers area // stored in memory (va_reg pointer). Otherwise va_ndx should point to the // position of the first variable argument on stack (va_stk pointer). if (ArgWords < 6) { VAIndex = DAG.getConstant(ArgWords * 4, DL, MVT::i32); } else { VAIndex = DAG.getConstant(32, DL, MVT::i32); } SDValue FrameIndex = DAG.getFrameIndex(XtensaFI->getVarArgsInRegsFrameIndex(), PtrVT); uint64_t FrameOffset = PtrVT.getStoreSize(); const Value *SV = cast(Op.getOperand(2))->getValue(); // Store pointer to arguments given on stack (va_stk) SDValue StackPtr = DAG.getNode(ISD::SUB, DL, PtrVT, StackOffsetFI, DAG.getConstant(32, DL, PtrVT)); SDValue StoreStackPtr = DAG.getStore(Chain, DL, StackPtr, Addr, MachinePointerInfo(SV)); uint64_t NextOffset = FrameOffset; SDValue NextPtr = DAG.getObjectPtrOffset(DL, Addr, TypeSize::getFixed(NextOffset)); // Store pointer to arguments given on registers (va_reg) SDValue StoreRegPtr = DAG.getStore(StoreStackPtr, DL, FrameIndex, NextPtr, MachinePointerInfo(SV, NextOffset)); NextOffset += FrameOffset; NextPtr = DAG.getObjectPtrOffset(DL, Addr, TypeSize::getFixed(NextOffset)); // Store third word : position in bytes of the first VA argument (va_ndx) return DAG.getStore(StoreRegPtr, DL, VAIndex, NextPtr, MachinePointerInfo(SV, NextOffset)); } SDValue XtensaTargetLowering::LowerVACOPY(SDValue Op, SelectionDAG &DAG) const { // Size of the va_list_tag structure constexpr unsigned VAListSize = 3 * 4; SDValue Chain = Op.getOperand(0); SDValue DstPtr = Op.getOperand(1); SDValue SrcPtr = Op.getOperand(2); const Value *DstSV = cast(Op.getOperand(3))->getValue(); const Value *SrcSV = cast(Op.getOperand(4))->getValue(); SDLoc DL(Op); return DAG.getMemcpy(Chain, DL, DstPtr, SrcPtr, DAG.getConstant(VAListSize, SDLoc(Op), MVT::i32), Align(4), /*isVolatile*/ false, /*AlwaysInline*/ true, /*CI=*/nullptr, std::nullopt, MachinePointerInfo(DstSV), MachinePointerInfo(SrcSV)); } SDValue XtensaTargetLowering::LowerVAARG(SDValue Op, SelectionDAG &DAG) const { SDNode *Node = Op.getNode(); EVT VT = Node->getValueType(0); Type *Ty = VT.getTypeForEVT(*DAG.getContext()); EVT PtrVT = Op.getValueType(); SDValue InChain = Node->getOperand(0); SDValue VAListPtr = Node->getOperand(1); const Value *SV = cast(Node->getOperand(2))->getValue(); SDLoc DL(Node); auto &TD = DAG.getDataLayout(); Align ArgAlignment = TD.getABITypeAlign(Ty); unsigned ArgAlignInBytes = ArgAlignment.value(); unsigned ArgSizeInBytes = TD.getTypeAllocSize(Ty); unsigned VASizeInBytes = llvm::alignTo(ArgSizeInBytes, 4); // va_stk SDValue VAStack = DAG.getLoad(MVT::i32, DL, InChain, VAListPtr, MachinePointerInfo()); InChain = VAStack.getValue(1); // va_reg SDValue VARegPtr = DAG.getObjectPtrOffset(DL, VAListPtr, TypeSize::getFixed(4)); SDValue VAReg = DAG.getLoad(MVT::i32, DL, InChain, VARegPtr, MachinePointerInfo()); InChain = VAReg.getValue(1); // va_ndx SDValue VarArgIndexPtr = DAG.getObjectPtrOffset(DL, VARegPtr, TypeSize::getFixed(4)); SDValue VAIndex = DAG.getLoad(MVT::i32, DL, InChain, VarArgIndexPtr, MachinePointerInfo()); InChain = VAIndex.getValue(1); SDValue OrigIndex = VAIndex; if (ArgAlignInBytes > 4) { OrigIndex = DAG.getNode(ISD::ADD, DL, PtrVT, OrigIndex, DAG.getConstant(ArgAlignInBytes - 1, DL, MVT::i32)); OrigIndex = DAG.getNode(ISD::AND, DL, PtrVT, OrigIndex, DAG.getSignedConstant(-ArgAlignInBytes, DL, MVT::i32)); } VAIndex = DAG.getNode(ISD::ADD, DL, PtrVT, OrigIndex, DAG.getConstant(VASizeInBytes, DL, MVT::i32)); SDValue CC = DAG.getSetCC(DL, MVT::i32, OrigIndex, DAG.getConstant(6 * 4, DL, MVT::i32), ISD::SETLE); SDValue StkIndex = DAG.getNode(ISD::ADD, DL, PtrVT, VAIndex, DAG.getConstant(32 + VASizeInBytes, DL, MVT::i32)); CC = DAG.getSetCC(DL, MVT::i32, VAIndex, DAG.getConstant(6 * 4, DL, MVT::i32), ISD::SETLE); SDValue Array = DAG.getNode(ISD::SELECT, DL, MVT::i32, CC, VAReg, VAStack); VAIndex = DAG.getNode(ISD::SELECT, DL, MVT::i32, CC, VAIndex, StkIndex); CC = DAG.getSetCC(DL, MVT::i32, VAIndex, DAG.getConstant(6 * 4, DL, MVT::i32), ISD::SETLE); SDValue VAIndexStore = DAG.getStore(InChain, DL, VAIndex, VarArgIndexPtr, MachinePointerInfo(SV)); InChain = VAIndexStore; SDValue Addr = DAG.getNode(ISD::SUB, DL, PtrVT, VAIndex, DAG.getConstant(VASizeInBytes, DL, MVT::i32)); Addr = DAG.getNode(ISD::ADD, DL, PtrVT, Array, Addr); return DAG.getLoad(VT, DL, InChain, Addr, MachinePointerInfo()); } 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.getSignedConstant(-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.getSignedConstant(-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(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::RETURNADDR: return LowerRETURNADDR(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::FRAMEADDR: return LowerFRAMEADDR(Op, DAG); case ISD::DYNAMIC_STACKALLOC: return LowerDYNAMIC_STACKALLOC(Op, DAG); case ISD::VASTART: return LowerVASTART(Op, DAG); case ISD::VAARG: return LowerVAARG(Op, DAG); case ISD::VACOPY: return LowerVACOPY(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(); const XtensaInstrInfo &TII = *Subtarget.getInstrInfo(); switch (MI.getOpcode()) { case Xtensa::SELECT: return emitSelectCC(MI, MBB); case Xtensa::S8I: case Xtensa::S16I: case Xtensa::S32I: case Xtensa::S32I_N: case Xtensa::L8UI: case Xtensa::L16SI: case Xtensa::L16UI: case Xtensa::L32I: case Xtensa::L32I_N: { // Insert memory wait instruction "memw" before volatile load/store as it is // implemented in gcc. If memoperands is empty then assume that it aslo // maybe volatile load/store and insert "memw". if (MI.memoperands_empty() || (*MI.memoperands_begin())->isVolatile()) { BuildMI(*MBB, MI, DL, TII.get(Xtensa::MEMW)); } return MBB; } default: llvm_unreachable("Unexpected instr type to insert"); } }