| //===-- M68kISelLowering.cpp - M68k DAG Lowering Impl -----------*- C++ -*-===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// This file defines the interfaces that M68k uses to lower LLVM code into a |
| /// selection DAG. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "M68kISelLowering.h" |
| #include "M68kCallingConv.h" |
| #include "M68kMachineFunction.h" |
| #include "M68kSubtarget.h" |
| #include "M68kTargetMachine.h" |
| #include "M68kTargetObjectFile.h" |
| |
| #include "llvm/ADT/Statistic.h" |
| #include "llvm/CodeGen/CallingConvLower.h" |
| #include "llvm/CodeGen/MachineFrameInfo.h" |
| #include "llvm/CodeGen/MachineFunction.h" |
| #include "llvm/CodeGen/MachineInstrBuilder.h" |
| #include "llvm/CodeGen/MachineJumpTableInfo.h" |
| #include "llvm/CodeGen/MachineRegisterInfo.h" |
| #include "llvm/CodeGen/SelectionDAG.h" |
| #include "llvm/CodeGen/ValueTypes.h" |
| #include "llvm/IR/CallingConv.h" |
| #include "llvm/IR/DerivedTypes.h" |
| #include "llvm/IR/GlobalVariable.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/KnownBits.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "M68k-isel" |
| |
| STATISTIC(NumTailCalls, "Number of tail calls"); |
| |
| M68kTargetLowering::M68kTargetLowering(const M68kTargetMachine &TM, |
| const M68kSubtarget &STI) |
| : TargetLowering(TM), Subtarget(STI), TM(TM) { |
| |
| MVT PtrVT = MVT::i32; |
| |
| setBooleanContents(ZeroOrOneBooleanContent); |
| |
| auto *RegInfo = Subtarget.getRegisterInfo(); |
| setStackPointerRegisterToSaveRestore(RegInfo->getStackRegister()); |
| |
| // Set up the register classes. |
| addRegisterClass(MVT::i8, &M68k::DR8RegClass); |
| addRegisterClass(MVT::i16, &M68k::XR16RegClass); |
| addRegisterClass(MVT::i32, &M68k::XR32RegClass); |
| |
| for (auto 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); |
| } |
| |
| // We don't accept any truncstore of integer registers. |
| setTruncStoreAction(MVT::i64, MVT::i32, Expand); |
| setTruncStoreAction(MVT::i64, MVT::i16, Expand); |
| setTruncStoreAction(MVT::i64, MVT::i8, Expand); |
| setTruncStoreAction(MVT::i32, MVT::i16, Expand); |
| setTruncStoreAction(MVT::i32, MVT::i8, Expand); |
| setTruncStoreAction(MVT::i16, MVT::i8, Expand); |
| |
| setOperationAction({ISD::MUL, ISD::SDIV, ISD::UDIV}, MVT::i8, Promote); |
| setOperationAction({ISD::MUL, ISD::SDIV, ISD::UDIV}, MVT::i16, Legal); |
| if (Subtarget.atLeastM68020()) |
| setOperationAction({ISD::MUL, ISD::SDIV, ISD::UDIV}, MVT::i32, Legal); |
| else |
| setOperationAction({ISD::MUL, ISD::SDIV, ISD::UDIV}, MVT::i32, LibCall); |
| setOperationAction(ISD::MUL, MVT::i64, LibCall); |
| |
| for (auto OP : |
| {ISD::SREM, ISD::UREM, ISD::UDIVREM, ISD::SDIVREM, |
| ISD::MULHS, ISD::MULHU, ISD::UMUL_LOHI, ISD::SMUL_LOHI}) { |
| setOperationAction(OP, MVT::i8, Promote); |
| setOperationAction(OP, MVT::i16, Legal); |
| setOperationAction(OP, MVT::i32, LibCall); |
| } |
| |
| for (auto OP : {ISD::UMUL_LOHI, ISD::SMUL_LOHI}) { |
| setOperationAction(OP, MVT::i8, Expand); |
| setOperationAction(OP, MVT::i16, Expand); |
| } |
| |
| for (auto OP : {ISD::SMULO, ISD::UMULO}) { |
| setOperationAction(OP, MVT::i8, Custom); |
| setOperationAction(OP, MVT::i16, Custom); |
| setOperationAction(OP, MVT::i32, Custom); |
| } |
| |
| for (auto OP : {ISD::SHL_PARTS, ISD::SRA_PARTS, ISD::SRL_PARTS}) |
| setOperationAction(OP, MVT::i32, Custom); |
| |
| // Add/Sub overflow ops with MVT::Glues are lowered to CCR dependences. |
| for (auto VT : {MVT::i8, MVT::i16, MVT::i32}) { |
| setOperationAction(ISD::ADDC, VT, Custom); |
| setOperationAction(ISD::ADDE, VT, Custom); |
| setOperationAction(ISD::SUBC, VT, Custom); |
| setOperationAction(ISD::SUBE, VT, Custom); |
| } |
| |
| // SADDO and friends are legal with this setup, i hope |
| for (auto VT : {MVT::i8, MVT::i16, MVT::i32}) { |
| setOperationAction(ISD::SADDO, VT, Custom); |
| setOperationAction(ISD::UADDO, VT, Custom); |
| setOperationAction(ISD::SSUBO, VT, Custom); |
| setOperationAction(ISD::USUBO, VT, Custom); |
| } |
| |
| setOperationAction(ISD::BR_JT, MVT::Other, Expand); |
| setOperationAction(ISD::BRCOND, MVT::Other, Custom); |
| |
| for (auto VT : {MVT::i8, MVT::i16, MVT::i32}) { |
| setOperationAction(ISD::BR_CC, VT, Expand); |
| setOperationAction(ISD::SELECT, VT, Custom); |
| setOperationAction(ISD::SELECT_CC, VT, Expand); |
| setOperationAction(ISD::SETCC, VT, Custom); |
| setOperationAction(ISD::SETCCCARRY, VT, Custom); |
| } |
| |
| for (auto VT : {MVT::i8, MVT::i16, MVT::i32}) { |
| setOperationAction(ISD::BSWAP, VT, Expand); |
| setOperationAction(ISD::CTTZ, VT, Expand); |
| setOperationAction(ISD::CTLZ, VT, Expand); |
| setOperationAction(ISD::CTPOP, VT, Expand); |
| } |
| |
| setOperationAction(ISD::ConstantPool, MVT::i32, Custom); |
| setOperationAction(ISD::JumpTable, MVT::i32, Custom); |
| setOperationAction(ISD::GlobalAddress, MVT::i32, Custom); |
| setOperationAction(ISD::GlobalTLSAddress, MVT::i32, Custom); |
| setOperationAction(ISD::ExternalSymbol, MVT::i32, Custom); |
| setOperationAction(ISD::BlockAddress, MVT::i32, Custom); |
| |
| setOperationAction(ISD::VASTART, MVT::Other, Custom); |
| setOperationAction(ISD::VAEND, MVT::Other, Expand); |
| setOperationAction(ISD::VAARG, MVT::Other, Expand); |
| setOperationAction(ISD::VACOPY, MVT::Other, Expand); |
| |
| setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); |
| setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); |
| |
| setOperationAction(ISD::DYNAMIC_STACKALLOC, PtrVT, Custom); |
| |
| computeRegisterProperties(STI.getRegisterInfo()); |
| |
| // We lower the `atomic-compare-and-swap` to `__sync_val_compare_and_swap` |
| // for subtarget < M68020 |
| setMaxAtomicSizeInBitsSupported(32); |
| setOperationAction(ISD::ATOMIC_CMP_SWAP, {MVT::i8, MVT::i16, MVT::i32}, |
| Subtarget.atLeastM68020() ? Legal : LibCall); |
| |
| setOperationAction(ISD::ATOMIC_FENCE, MVT::Other, Custom); |
| |
| // M68k does not have native read-modify-write support, so expand all of them |
| // to `__sync_fetch_*` for target < M68020, otherwise expand to CmpxChg. |
| // See `shouldExpandAtomicRMWInIR` below. |
| setOperationAction( |
| { |
| ISD::ATOMIC_LOAD_ADD, |
| ISD::ATOMIC_LOAD_SUB, |
| ISD::ATOMIC_LOAD_AND, |
| ISD::ATOMIC_LOAD_OR, |
| ISD::ATOMIC_LOAD_XOR, |
| ISD::ATOMIC_LOAD_NAND, |
| ISD::ATOMIC_LOAD_MIN, |
| ISD::ATOMIC_LOAD_MAX, |
| ISD::ATOMIC_LOAD_UMIN, |
| ISD::ATOMIC_LOAD_UMAX, |
| ISD::ATOMIC_SWAP, |
| }, |
| {MVT::i8, MVT::i16, MVT::i32}, LibCall); |
| |
| setMinFunctionAlignment(Align(2)); |
| } |
| |
| TargetLoweringBase::AtomicExpansionKind |
| M68kTargetLowering::shouldExpandAtomicRMWInIR(AtomicRMWInst *RMW) const { |
| return Subtarget.atLeastM68020() |
| ? TargetLoweringBase::AtomicExpansionKind::CmpXChg |
| : TargetLoweringBase::AtomicExpansionKind::None; |
| } |
| |
| Register |
| M68kTargetLowering::getExceptionPointerRegister(const Constant *) const { |
| return M68k::D0; |
| } |
| |
| Register |
| M68kTargetLowering::getExceptionSelectorRegister(const Constant *) const { |
| return M68k::D1; |
| } |
| |
| InlineAsm::ConstraintCode |
| M68kTargetLowering::getInlineAsmMemConstraint(StringRef ConstraintCode) const { |
| return StringSwitch<InlineAsm::ConstraintCode>(ConstraintCode) |
| .Case("Q", InlineAsm::ConstraintCode::Q) |
| // We borrow ConstraintCode::Um for 'U'. |
| .Case("U", InlineAsm::ConstraintCode::Um) |
| .Default(TargetLowering::getInlineAsmMemConstraint(ConstraintCode)); |
| } |
| |
| EVT M68kTargetLowering::getSetCCResultType(const DataLayout &DL, |
| LLVMContext &Context, EVT VT) const { |
| // M68k SETcc producess either 0x00 or 0xFF |
| return MVT::i8; |
| } |
| |
| MVT M68kTargetLowering::getScalarShiftAmountTy(const DataLayout &DL, |
| EVT Ty) const { |
| if (Ty.isSimple()) { |
| return Ty.getSimpleVT(); |
| } |
| return MVT::getIntegerVT(DL.getPointerSizeInBits(0)); |
| } |
| |
| #include "M68kGenCallingConv.inc" |
| |
| enum StructReturnType { NotStructReturn, RegStructReturn, StackStructReturn }; |
| |
| static StructReturnType |
| callIsStructReturn(const SmallVectorImpl<ISD::OutputArg> &Outs) { |
| if (Outs.empty()) |
| return NotStructReturn; |
| |
| const ISD::ArgFlagsTy &Flags = Outs[0].Flags; |
| if (!Flags.isSRet()) |
| return NotStructReturn; |
| if (Flags.isInReg()) |
| return RegStructReturn; |
| return StackStructReturn; |
| } |
| |
| /// Determines whether a function uses struct return semantics. |
| static StructReturnType |
| argsAreStructReturn(const SmallVectorImpl<ISD::InputArg> &Ins) { |
| if (Ins.empty()) |
| return NotStructReturn; |
| |
| const ISD::ArgFlagsTy &Flags = Ins[0].Flags; |
| if (!Flags.isSRet()) |
| return NotStructReturn; |
| if (Flags.isInReg()) |
| return RegStructReturn; |
| return StackStructReturn; |
| } |
| |
| /// Make a copy of an aggregate at address specified by "Src" to address |
| /// "Dst" with size and alignment information specified by the specific |
| /// parameter attribute. The copy will be passed as a byval function parameter. |
| static SDValue CreateCopyOfByValArgument(SDValue Src, SDValue Dst, |
| SDValue Chain, ISD::ArgFlagsTy Flags, |
| SelectionDAG &DAG, const SDLoc &DL) { |
| SDValue SizeNode = DAG.getConstant(Flags.getByValSize(), DL, MVT::i32); |
| |
| return DAG.getMemcpy( |
| Chain, DL, Dst, Src, SizeNode, Flags.getNonZeroByValAlign(), |
| /*isVolatile=*/false, /*AlwaysInline=*/true, |
| /*isTailCall=*/false, MachinePointerInfo(), MachinePointerInfo()); |
| } |
| |
| /// Return true if the calling convention is one that we can guarantee TCO for. |
| static bool canGuaranteeTCO(CallingConv::ID CC) { return false; } |
| |
| /// Return true if we might ever do TCO for calls with this calling convention. |
| static bool mayTailCallThisCC(CallingConv::ID CC) { |
| switch (CC) { |
| // C calling conventions: |
| case CallingConv::C: |
| return true; |
| default: |
| return canGuaranteeTCO(CC); |
| } |
| } |
| |
| /// Return true if the function is being made into a tailcall target by |
| /// changing its ABI. |
| static bool shouldGuaranteeTCO(CallingConv::ID CC, bool GuaranteedTailCallOpt) { |
| return GuaranteedTailCallOpt && canGuaranteeTCO(CC); |
| } |
| |
| /// Return true if the given stack call argument is already available in the |
| /// same position (relatively) of the caller's incoming argument stack. |
| static bool MatchingStackOffset(SDValue Arg, unsigned Offset, |
| ISD::ArgFlagsTy Flags, MachineFrameInfo &MFI, |
| const MachineRegisterInfo *MRI, |
| const M68kInstrInfo *TII, |
| const CCValAssign &VA) { |
| unsigned Bytes = Arg.getValueType().getSizeInBits() / 8; |
| |
| for (;;) { |
| // Look through nodes that don't alter the bits of the incoming value. |
| unsigned Op = Arg.getOpcode(); |
| if (Op == ISD::ZERO_EXTEND || Op == ISD::ANY_EXTEND || Op == ISD::BITCAST) { |
| Arg = Arg.getOperand(0); |
| continue; |
| } |
| if (Op == ISD::TRUNCATE) { |
| const SDValue &TruncInput = Arg.getOperand(0); |
| if (TruncInput.getOpcode() == ISD::AssertZext && |
| cast<VTSDNode>(TruncInput.getOperand(1))->getVT() == |
| Arg.getValueType()) { |
| Arg = TruncInput.getOperand(0); |
| continue; |
| } |
| } |
| break; |
| } |
| |
| int FI = INT_MAX; |
| if (Arg.getOpcode() == ISD::CopyFromReg) { |
| Register VR = cast<RegisterSDNode>(Arg.getOperand(1))->getReg(); |
| if (!Register::isVirtualRegister(VR)) |
| return false; |
| MachineInstr *Def = MRI->getVRegDef(VR); |
| if (!Def) |
| return false; |
| if (!Flags.isByVal()) { |
| if (!TII->isLoadFromStackSlot(*Def, FI)) |
| return false; |
| } else { |
| unsigned Opcode = Def->getOpcode(); |
| if ((Opcode == M68k::LEA32p || Opcode == M68k::LEA32f) && |
| Def->getOperand(1).isFI()) { |
| FI = Def->getOperand(1).getIndex(); |
| Bytes = Flags.getByValSize(); |
| } else |
| return false; |
| } |
| } else if (auto *Ld = dyn_cast<LoadSDNode>(Arg)) { |
| if (Flags.isByVal()) |
| // ByVal argument is passed in as a pointer but it's now being |
| // dereferenced. e.g. |
| // define @foo(%struct.X* %A) { |
| // tail call @bar(%struct.X* byval %A) |
| // } |
| return false; |
| SDValue Ptr = Ld->getBasePtr(); |
| FrameIndexSDNode *FINode = dyn_cast<FrameIndexSDNode>(Ptr); |
| if (!FINode) |
| return false; |
| FI = FINode->getIndex(); |
| } else if (Arg.getOpcode() == ISD::FrameIndex && Flags.isByVal()) { |
| FrameIndexSDNode *FINode = cast<FrameIndexSDNode>(Arg); |
| FI = FINode->getIndex(); |
| Bytes = Flags.getByValSize(); |
| } else |
| return false; |
| |
| assert(FI != INT_MAX); |
| if (!MFI.isFixedObjectIndex(FI)) |
| return false; |
| |
| if (Offset != MFI.getObjectOffset(FI)) |
| return false; |
| |
| if (VA.getLocVT().getSizeInBits() > Arg.getValueType().getSizeInBits()) { |
| // If the argument location is wider than the argument type, check that any |
| // extension flags match. |
| if (Flags.isZExt() != MFI.isObjectZExt(FI) || |
| Flags.isSExt() != MFI.isObjectSExt(FI)) { |
| return false; |
| } |
| } |
| |
| return Bytes == MFI.getObjectSize(FI); |
| } |
| |
| SDValue |
| M68kTargetLowering::getReturnAddressFrameIndex(SelectionDAG &DAG) const { |
| MachineFunction &MF = DAG.getMachineFunction(); |
| M68kMachineFunctionInfo *FuncInfo = MF.getInfo<M68kMachineFunctionInfo>(); |
| int ReturnAddrIndex = FuncInfo->getRAIndex(); |
| |
| if (ReturnAddrIndex == 0) { |
| // Set up a frame object for the return address. |
| unsigned SlotSize = Subtarget.getSlotSize(); |
| ReturnAddrIndex = MF.getFrameInfo().CreateFixedObject( |
| SlotSize, -(int64_t)SlotSize, false); |
| FuncInfo->setRAIndex(ReturnAddrIndex); |
| } |
| |
| return DAG.getFrameIndex(ReturnAddrIndex, getPointerTy(DAG.getDataLayout())); |
| } |
| |
| SDValue M68kTargetLowering::EmitTailCallLoadRetAddr(SelectionDAG &DAG, |
| SDValue &OutRetAddr, |
| SDValue Chain, |
| bool IsTailCall, int FPDiff, |
| const SDLoc &DL) const { |
| EVT VT = getPointerTy(DAG.getDataLayout()); |
| OutRetAddr = getReturnAddressFrameIndex(DAG); |
| |
| // Load the "old" Return address. |
| OutRetAddr = DAG.getLoad(VT, DL, Chain, OutRetAddr, MachinePointerInfo()); |
| return SDValue(OutRetAddr.getNode(), 1); |
| } |
| |
| SDValue M68kTargetLowering::EmitTailCallStoreRetAddr( |
| SelectionDAG &DAG, MachineFunction &MF, SDValue Chain, SDValue RetFI, |
| EVT PtrVT, unsigned SlotSize, int FPDiff, const SDLoc &DL) const { |
| if (!FPDiff) |
| return Chain; |
| |
| // Calculate the new stack slot for the return address. |
| int NewFO = MF.getFrameInfo().CreateFixedObject( |
| SlotSize, (int64_t)FPDiff - SlotSize, false); |
| |
| SDValue NewFI = DAG.getFrameIndex(NewFO, PtrVT); |
| // Store the return address to the appropriate stack slot. |
| Chain = DAG.getStore( |
| Chain, DL, RetFI, NewFI, |
| MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), NewFO)); |
| return Chain; |
| } |
| |
| SDValue |
| M68kTargetLowering::LowerMemArgument(SDValue Chain, CallingConv::ID CallConv, |
| const SmallVectorImpl<ISD::InputArg> &Ins, |
| const SDLoc &DL, SelectionDAG &DAG, |
| const CCValAssign &VA, |
| MachineFrameInfo &MFI, |
| unsigned ArgIdx) const { |
| // Create the nodes corresponding to a load from this parameter slot. |
| ISD::ArgFlagsTy Flags = Ins[ArgIdx].Flags; |
| EVT ValVT; |
| |
| // If value is passed by pointer we have address passed instead of the value |
| // itself. |
| if (VA.getLocInfo() == CCValAssign::Indirect) |
| ValVT = VA.getLocVT(); |
| else |
| ValVT = VA.getValVT(); |
| |
| // Because we are dealing with BE architecture we need to offset loading of |
| // partial types |
| int Offset = VA.getLocMemOffset(); |
| if (VA.getValVT() == MVT::i8) { |
| Offset += 3; |
| } else if (VA.getValVT() == MVT::i16) { |
| Offset += 2; |
| } |
| |
| // TODO Interrupt handlers |
| // Calculate SP offset of interrupt parameter, re-arrange the slot normally |
| // taken by a return address. |
| |
| // FIXME For now, all byval parameter objects are marked mutable. This can |
| // be changed with more analysis. In case of tail call optimization mark all |
| // arguments mutable. Since they could be overwritten by lowering of arguments |
| // in case of a tail call. |
| bool AlwaysUseMutable = shouldGuaranteeTCO( |
| CallConv, DAG.getTarget().Options.GuaranteedTailCallOpt); |
| bool IsImmutable = !AlwaysUseMutable && !Flags.isByVal(); |
| |
| if (Flags.isByVal()) { |
| unsigned Bytes = Flags.getByValSize(); |
| if (Bytes == 0) |
| Bytes = 1; // Don't create zero-sized stack objects. |
| int FI = MFI.CreateFixedObject(Bytes, Offset, IsImmutable); |
| // TODO Interrupt handlers |
| // Adjust SP offset of interrupt parameter. |
| return DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); |
| } else { |
| int FI = |
| MFI.CreateFixedObject(ValVT.getSizeInBits() / 8, Offset, IsImmutable); |
| |
| // Set SExt or ZExt flag. |
| if (VA.getLocInfo() == CCValAssign::ZExt) { |
| MFI.setObjectZExt(FI, true); |
| } else if (VA.getLocInfo() == CCValAssign::SExt) { |
| MFI.setObjectSExt(FI, true); |
| } |
| |
| // TODO Interrupt handlers |
| // Adjust SP offset of interrupt parameter. |
| |
| SDValue FIN = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); |
| SDValue Val = DAG.getLoad( |
| ValVT, DL, Chain, FIN, |
| MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI)); |
| return VA.isExtInLoc() ? DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), Val) |
| : Val; |
| } |
| } |
| |
| SDValue M68kTargetLowering::LowerMemOpCallTo(SDValue Chain, SDValue StackPtr, |
| SDValue Arg, const SDLoc &DL, |
| SelectionDAG &DAG, |
| const CCValAssign &VA, |
| ISD::ArgFlagsTy Flags) const { |
| unsigned LocMemOffset = VA.getLocMemOffset(); |
| SDValue PtrOff = DAG.getIntPtrConstant(LocMemOffset, DL); |
| PtrOff = DAG.getNode(ISD::ADD, DL, getPointerTy(DAG.getDataLayout()), |
| StackPtr, PtrOff); |
| if (Flags.isByVal()) |
| return CreateCopyOfByValArgument(Arg, PtrOff, Chain, Flags, DAG, DL); |
| |
| return DAG.getStore( |
| Chain, DL, Arg, PtrOff, |
| MachinePointerInfo::getStack(DAG.getMachineFunction(), LocMemOffset)); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Call |
| //===----------------------------------------------------------------------===// |
| |
| SDValue M68kTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, |
| SmallVectorImpl<SDValue> &InVals) const { |
| SelectionDAG &DAG = CLI.DAG; |
| SDLoc &DL = CLI.DL; |
| SmallVectorImpl<ISD::OutputArg> &Outs = CLI.Outs; |
| SmallVectorImpl<SDValue> &OutVals = CLI.OutVals; |
| SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins; |
| SDValue Chain = CLI.Chain; |
| SDValue Callee = CLI.Callee; |
| CallingConv::ID CallConv = CLI.CallConv; |
| bool &IsTailCall = CLI.IsTailCall; |
| bool IsVarArg = CLI.IsVarArg; |
| |
| MachineFunction &MF = DAG.getMachineFunction(); |
| StructReturnType SR = callIsStructReturn(Outs); |
| bool IsSibcall = false; |
| M68kMachineFunctionInfo *MFI = MF.getInfo<M68kMachineFunctionInfo>(); |
| // const M68kRegisterInfo *TRI = Subtarget.getRegisterInfo(); |
| |
| if (CallConv == CallingConv::M68k_INTR) |
| report_fatal_error("M68k interrupts may not be called directly"); |
| |
| auto Attr = MF.getFunction().getFnAttribute("disable-tail-calls"); |
| if (Attr.getValueAsBool()) |
| IsTailCall = false; |
| |
| // FIXME Add tailcalls support |
| |
| bool IsMustTail = CLI.CB && CLI.CB->isMustTailCall(); |
| if (IsMustTail) { |
| // Force this to be a tail call. The verifier rules are enough to ensure |
| // that we can lower this successfully without moving the return address |
| // around. |
| IsTailCall = true; |
| } else if (IsTailCall) { |
| // Check if it's really possible to do a tail call. |
| IsTailCall = IsEligibleForTailCallOptimization( |
| Callee, CallConv, IsVarArg, SR != NotStructReturn, |
| MF.getFunction().hasStructRetAttr(), CLI.RetTy, Outs, OutVals, Ins, |
| DAG); |
| |
| // Sibcalls are automatically detected tailcalls which do not require |
| // ABI changes. |
| if (!MF.getTarget().Options.GuaranteedTailCallOpt && IsTailCall) |
| IsSibcall = true; |
| |
| if (IsTailCall) |
| ++NumTailCalls; |
| } |
| |
| assert(!(IsVarArg && canGuaranteeTCO(CallConv)) && |
| "Var args not supported with calling convention fastcc"); |
| |
| // Analyze operands of the call, assigning locations to each operand. |
| SmallVector<CCValAssign, 16> ArgLocs; |
| SmallVector<Type *, 4> ArgTypes; |
| for (const auto &Arg : CLI.getArgs()) |
| ArgTypes.emplace_back(Arg.Ty); |
| M68kCCState CCInfo(ArgTypes, CallConv, IsVarArg, MF, ArgLocs, |
| *DAG.getContext()); |
| CCInfo.AnalyzeCallOperands(Outs, CC_M68k); |
| |
| // Get a count of how many bytes are to be pushed on the stack. |
| unsigned NumBytes = CCInfo.getAlignedCallFrameSize(); |
| if (IsSibcall) { |
| // This is a sibcall. The memory operands are available in caller's |
| // own caller's stack. |
| NumBytes = 0; |
| } else if (MF.getTarget().Options.GuaranteedTailCallOpt && |
| canGuaranteeTCO(CallConv)) { |
| NumBytes = GetAlignedArgumentStackSize(NumBytes, DAG); |
| } |
| |
| int FPDiff = 0; |
| if (IsTailCall && !IsSibcall && !IsMustTail) { |
| // Lower arguments at fp - stackoffset + fpdiff. |
| unsigned NumBytesCallerPushed = MFI->getBytesToPopOnReturn(); |
| |
| FPDiff = NumBytesCallerPushed - NumBytes; |
| |
| // Set the delta of movement of the returnaddr stackslot. |
| // But only set if delta is greater than previous delta. |
| if (FPDiff < MFI->getTCReturnAddrDelta()) |
| MFI->setTCReturnAddrDelta(FPDiff); |
| } |
| |
| unsigned NumBytesToPush = NumBytes; |
| unsigned NumBytesToPop = NumBytes; |
| |
| // If we have an inalloca argument, all stack space has already been allocated |
| // for us and be right at the top of the stack. We don't support multiple |
| // arguments passed in memory when using inalloca. |
| if (!Outs.empty() && Outs.back().Flags.isInAlloca()) { |
| NumBytesToPush = 0; |
| if (!ArgLocs.back().isMemLoc()) |
| report_fatal_error("cannot use inalloca attribute on a register " |
| "parameter"); |
| if (ArgLocs.back().getLocMemOffset() != 0) |
| report_fatal_error("any parameter with the inalloca attribute must be " |
| "the only memory argument"); |
| } |
| |
| if (!IsSibcall) |
| Chain = DAG.getCALLSEQ_START(Chain, NumBytesToPush, |
| NumBytes - NumBytesToPush, DL); |
| |
| SDValue RetFI; |
| // Load return address for tail calls. |
| if (IsTailCall && FPDiff) |
| Chain = EmitTailCallLoadRetAddr(DAG, RetFI, Chain, IsTailCall, FPDiff, DL); |
| |
| SmallVector<std::pair<unsigned, SDValue>, 8> RegsToPass; |
| SmallVector<SDValue, 8> MemOpChains; |
| SDValue StackPtr; |
| |
| // Walk the register/memloc assignments, inserting copies/loads. In the case |
| // of tail call optimization arguments are handle later. |
| const M68kRegisterInfo *RegInfo = Subtarget.getRegisterInfo(); |
| for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { |
| ISD::ArgFlagsTy Flags = Outs[i].Flags; |
| |
| // Skip inalloca arguments, they have already been written. |
| if (Flags.isInAlloca()) |
| continue; |
| |
| CCValAssign &VA = ArgLocs[i]; |
| EVT RegVT = VA.getLocVT(); |
| SDValue Arg = OutVals[i]; |
| bool IsByVal = Flags.isByVal(); |
| |
| // Promote the value if needed. |
| switch (VA.getLocInfo()) { |
| default: |
| llvm_unreachable("Unknown loc info!"); |
| case CCValAssign::Full: |
| break; |
| case CCValAssign::SExt: |
| Arg = DAG.getNode(ISD::SIGN_EXTEND, DL, RegVT, Arg); |
| break; |
| case CCValAssign::ZExt: |
| Arg = DAG.getNode(ISD::ZERO_EXTEND, DL, RegVT, Arg); |
| break; |
| case CCValAssign::AExt: |
| Arg = DAG.getNode(ISD::ANY_EXTEND, DL, RegVT, Arg); |
| break; |
| case CCValAssign::BCvt: |
| Arg = DAG.getBitcast(RegVT, Arg); |
| break; |
| case CCValAssign::Indirect: { |
| // Store the argument. |
| SDValue SpillSlot = DAG.CreateStackTemporary(VA.getValVT()); |
| int FI = cast<FrameIndexSDNode>(SpillSlot)->getIndex(); |
| Chain = DAG.getStore( |
| Chain, DL, Arg, SpillSlot, |
| MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI)); |
| Arg = SpillSlot; |
| break; |
| } |
| } |
| |
| if (VA.isRegLoc()) { |
| RegsToPass.push_back(std::make_pair(VA.getLocReg(), Arg)); |
| } else if (!IsSibcall && (!IsTailCall || IsByVal)) { |
| assert(VA.isMemLoc()); |
| if (!StackPtr.getNode()) { |
| StackPtr = DAG.getCopyFromReg(Chain, DL, RegInfo->getStackRegister(), |
| getPointerTy(DAG.getDataLayout())); |
| } |
| MemOpChains.push_back( |
| LowerMemOpCallTo(Chain, StackPtr, Arg, DL, DAG, VA, Flags)); |
| } |
| } |
| |
| if (!MemOpChains.empty()) |
| Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, MemOpChains); |
| |
| // FIXME Make sure PIC style GOT works as expected |
| // The only time GOT is really needed is for Medium-PIC static data |
| // otherwise we are happy with pc-rel or static references |
| |
| if (IsVarArg && IsMustTail) { |
| const auto &Forwards = MFI->getForwardedMustTailRegParms(); |
| for (const auto &F : Forwards) { |
| SDValue Val = DAG.getCopyFromReg(Chain, DL, F.VReg, F.VT); |
| RegsToPass.push_back(std::make_pair(unsigned(F.PReg), Val)); |
| } |
| } |
| |
| // For tail calls lower the arguments to the 'real' stack slots. Sibcalls |
| // don't need this because the eligibility check rejects calls that require |
| // shuffling arguments passed in memory. |
| if (!IsSibcall && IsTailCall) { |
| // Force all the incoming stack arguments to be loaded from the stack |
| // before any new outgoing arguments are stored to the stack, because the |
| // outgoing stack slots may alias the incoming argument stack slots, and |
| // the alias isn't otherwise explicit. This is slightly more conservative |
| // than necessary, because it means that each store effectively depends |
| // on every argument instead of just those arguments it would clobber. |
| SDValue ArgChain = DAG.getStackArgumentTokenFactor(Chain); |
| |
| SmallVector<SDValue, 8> MemOpChains2; |
| SDValue FIN; |
| int FI = 0; |
| for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { |
| CCValAssign &VA = ArgLocs[i]; |
| if (VA.isRegLoc()) |
| continue; |
| assert(VA.isMemLoc()); |
| SDValue Arg = OutVals[i]; |
| ISD::ArgFlagsTy Flags = Outs[i].Flags; |
| // Skip inalloca arguments. They don't require any work. |
| if (Flags.isInAlloca()) |
| continue; |
| // Create frame index. |
| int32_t Offset = VA.getLocMemOffset() + FPDiff; |
| uint32_t OpSize = (VA.getLocVT().getSizeInBits() + 7) / 8; |
| FI = MF.getFrameInfo().CreateFixedObject(OpSize, Offset, true); |
| FIN = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); |
| |
| if (Flags.isByVal()) { |
| // Copy relative to framepointer. |
| SDValue Source = DAG.getIntPtrConstant(VA.getLocMemOffset(), DL); |
| if (!StackPtr.getNode()) { |
| StackPtr = DAG.getCopyFromReg(Chain, DL, RegInfo->getStackRegister(), |
| getPointerTy(DAG.getDataLayout())); |
| } |
| Source = DAG.getNode(ISD::ADD, DL, getPointerTy(DAG.getDataLayout()), |
| StackPtr, Source); |
| |
| MemOpChains2.push_back( |
| CreateCopyOfByValArgument(Source, FIN, ArgChain, Flags, DAG, DL)); |
| } else { |
| // Store relative to framepointer. |
| MemOpChains2.push_back(DAG.getStore( |
| ArgChain, DL, Arg, FIN, |
| MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI))); |
| } |
| } |
| |
| if (!MemOpChains2.empty()) |
| Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, MemOpChains2); |
| |
| // Store the return address to the appropriate stack slot. |
| Chain = EmitTailCallStoreRetAddr(DAG, MF, Chain, RetFI, |
| getPointerTy(DAG.getDataLayout()), |
| Subtarget.getSlotSize(), FPDiff, DL); |
| } |
| |
| // Build a sequence of copy-to-reg nodes chained together with token chain |
| // and flag operands which copy the outgoing args into registers. |
| SDValue InGlue; |
| for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) { |
| Chain = DAG.getCopyToReg(Chain, DL, RegsToPass[i].first, |
| RegsToPass[i].second, InGlue); |
| InGlue = Chain.getValue(1); |
| } |
| |
| if (Callee->getOpcode() == ISD::GlobalAddress) { |
| // If the callee is a GlobalAddress node (quite common, every direct call |
| // is) turn it into a TargetGlobalAddress node so that legalize doesn't hack |
| // it. |
| GlobalAddressSDNode *G = cast<GlobalAddressSDNode>(Callee); |
| |
| // We should use extra load for direct calls to dllimported functions in |
| // non-JIT mode. |
| const GlobalValue *GV = G->getGlobal(); |
| if (!GV->hasDLLImportStorageClass()) { |
| unsigned char OpFlags = Subtarget.classifyGlobalFunctionReference(GV); |
| |
| Callee = DAG.getTargetGlobalAddress( |
| GV, DL, getPointerTy(DAG.getDataLayout()), G->getOffset(), OpFlags); |
| |
| if (OpFlags == M68kII::MO_GOTPCREL) { |
| |
| // Add a wrapper. |
| Callee = DAG.getNode(M68kISD::WrapperPC, DL, |
| getPointerTy(DAG.getDataLayout()), Callee); |
| |
| // Add extra indirection |
| Callee = DAG.getLoad( |
| getPointerTy(DAG.getDataLayout()), DL, DAG.getEntryNode(), Callee, |
| MachinePointerInfo::getGOT(DAG.getMachineFunction())); |
| } |
| } |
| } else if (ExternalSymbolSDNode *S = dyn_cast<ExternalSymbolSDNode>(Callee)) { |
| const Module *Mod = DAG.getMachineFunction().getFunction().getParent(); |
| unsigned char OpFlags = |
| Subtarget.classifyGlobalFunctionReference(nullptr, *Mod); |
| |
| Callee = DAG.getTargetExternalSymbol( |
| S->getSymbol(), getPointerTy(DAG.getDataLayout()), OpFlags); |
| } |
| |
| // Returns a chain & a flag for retval copy to use. |
| SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); |
| SmallVector<SDValue, 8> Ops; |
| |
| if (!IsSibcall && IsTailCall) { |
| Chain = DAG.getCALLSEQ_END(Chain, NumBytesToPop, 0, InGlue, DL); |
| InGlue = Chain.getValue(1); |
| } |
| |
| Ops.push_back(Chain); |
| Ops.push_back(Callee); |
| |
| if (IsTailCall) |
| Ops.push_back(DAG.getConstant(FPDiff, DL, MVT::i32)); |
| |
| // 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) |
| Ops.push_back(DAG.getRegister(RegsToPass[i].first, |
| RegsToPass[i].second.getValueType())); |
| |
| // Add a register mask operand representing the call-preserved registers. |
| const uint32_t *Mask = RegInfo->getCallPreservedMask(MF, CallConv); |
| assert(Mask && "Missing call preserved mask for calling convention"); |
| |
| Ops.push_back(DAG.getRegisterMask(Mask)); |
| |
| if (InGlue.getNode()) |
| Ops.push_back(InGlue); |
| |
| if (IsTailCall) { |
| MF.getFrameInfo().setHasTailCall(); |
| return DAG.getNode(M68kISD::TC_RETURN, DL, NodeTys, Ops); |
| } |
| |
| Chain = DAG.getNode(M68kISD::CALL, DL, NodeTys, Ops); |
| InGlue = Chain.getValue(1); |
| |
| // Create the CALLSEQ_END node. |
| unsigned NumBytesForCalleeToPop; |
| if (M68k::isCalleePop(CallConv, IsVarArg, |
| DAG.getTarget().Options.GuaranteedTailCallOpt)) { |
| NumBytesForCalleeToPop = NumBytes; // Callee pops everything |
| } else if (!canGuaranteeTCO(CallConv) && SR == StackStructReturn) { |
| // If this is a call to a struct-return function, the callee |
| // pops the hidden struct pointer, so we have to push it back. |
| NumBytesForCalleeToPop = 4; |
| } else { |
| NumBytesForCalleeToPop = 0; // Callee pops nothing. |
| } |
| |
| if (CLI.DoesNotReturn && !getTargetMachine().Options.TrapUnreachable) { |
| // No need to reset the stack after the call if the call doesn't return. To |
| // make the MI verify, we'll pretend the callee does it for us. |
| NumBytesForCalleeToPop = NumBytes; |
| } |
| |
| // Returns a flag for retval copy to use. |
| if (!IsSibcall) { |
| Chain = DAG.getCALLSEQ_END(Chain, NumBytesToPop, NumBytesForCalleeToPop, |
| InGlue, DL); |
| InGlue = Chain.getValue(1); |
| } |
| |
| // Handle result values, copying them out of physregs into vregs that we |
| // return. |
| return LowerCallResult(Chain, InGlue, CallConv, IsVarArg, Ins, DL, DAG, |
| InVals); |
| } |
| |
| SDValue M68kTargetLowering::LowerCallResult( |
| SDValue Chain, SDValue InGlue, CallingConv::ID CallConv, bool IsVarArg, |
| const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL, |
| SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const { |
| |
| // Assign locations to each value returned by this call. |
| SmallVector<CCValAssign, 16> RVLocs; |
| CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), RVLocs, |
| *DAG.getContext()); |
| CCInfo.AnalyzeCallResult(Ins, RetCC_M68k); |
| |
| // Copy all of the result registers out of their specified physreg. |
| for (unsigned i = 0, e = RVLocs.size(); i != e; ++i) { |
| CCValAssign &VA = RVLocs[i]; |
| EVT CopyVT = VA.getLocVT(); |
| |
| /// ??? is this correct? |
| Chain = DAG.getCopyFromReg(Chain, DL, VA.getLocReg(), CopyVT, InGlue) |
| .getValue(1); |
| SDValue Val = Chain.getValue(0); |
| |
| if (VA.isExtInLoc() && VA.getValVT().getScalarType() == MVT::i1) |
| Val = DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), Val); |
| |
| InGlue = Chain.getValue(2); |
| InVals.push_back(Val); |
| } |
| |
| return Chain; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Formal Arguments Calling Convention Implementation |
| //===----------------------------------------------------------------------===// |
| |
| SDValue M68kTargetLowering::LowerFormalArguments( |
| SDValue Chain, CallingConv::ID CCID, bool IsVarArg, |
| const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL, |
| SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const { |
| MachineFunction &MF = DAG.getMachineFunction(); |
| M68kMachineFunctionInfo *MMFI = MF.getInfo<M68kMachineFunctionInfo>(); |
| // const TargetFrameLowering &TFL = *Subtarget.getFrameLowering(); |
| |
| MachineFrameInfo &MFI = MF.getFrameInfo(); |
| |
| // Assign locations to all of the incoming arguments. |
| SmallVector<CCValAssign, 16> ArgLocs; |
| SmallVector<Type *, 4> ArgTypes; |
| for (const Argument &Arg : MF.getFunction().args()) |
| ArgTypes.emplace_back(Arg.getType()); |
| M68kCCState CCInfo(ArgTypes, CCID, IsVarArg, MF, ArgLocs, *DAG.getContext()); |
| |
| CCInfo.AnalyzeFormalArguments(Ins, CC_M68k); |
| |
| unsigned LastVal = ~0U; |
| SDValue ArgValue; |
| for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { |
| CCValAssign &VA = ArgLocs[i]; |
| assert(VA.getValNo() != LastVal && "Same value in different locations"); |
| |
| LastVal = VA.getValNo(); |
| |
| if (VA.isRegLoc()) { |
| EVT RegVT = VA.getLocVT(); |
| const TargetRegisterClass *RC; |
| if (RegVT == MVT::i32) |
| RC = &M68k::XR32RegClass; |
| else |
| llvm_unreachable("Unknown argument type!"); |
| |
| Register Reg = MF.addLiveIn(VA.getLocReg(), RC); |
| ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, RegVT); |
| |
| // If this is an 8 or 16-bit value, it is really passed promoted to 32 |
| // bits. Insert an assert[sz]ext to capture this, then truncate to the |
| // right size. |
| if (VA.getLocInfo() == CCValAssign::SExt) { |
| ArgValue = DAG.getNode(ISD::AssertSext, DL, RegVT, ArgValue, |
| DAG.getValueType(VA.getValVT())); |
| } else if (VA.getLocInfo() == CCValAssign::ZExt) { |
| ArgValue = DAG.getNode(ISD::AssertZext, DL, RegVT, ArgValue, |
| DAG.getValueType(VA.getValVT())); |
| } else if (VA.getLocInfo() == CCValAssign::BCvt) { |
| ArgValue = DAG.getBitcast(VA.getValVT(), ArgValue); |
| } |
| |
| if (VA.isExtInLoc()) { |
| ArgValue = DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), ArgValue); |
| } |
| } else { |
| assert(VA.isMemLoc()); |
| ArgValue = LowerMemArgument(Chain, CCID, Ins, DL, DAG, VA, MFI, i); |
| } |
| |
| // If value is passed via pointer - do a load. |
| // TODO Make sure this handling on indirect arguments is correct |
| if (VA.getLocInfo() == CCValAssign::Indirect) |
| ArgValue = |
| DAG.getLoad(VA.getValVT(), DL, Chain, ArgValue, MachinePointerInfo()); |
| |
| InVals.push_back(ArgValue); |
| } |
| |
| for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { |
| // Swift calling convention does not require we copy the sret argument |
| // into %D0 for the return. We don't set SRetReturnReg for Swift. |
| if (CCID == CallingConv::Swift) |
| continue; |
| |
| // ABI require that for returning structs by value we copy the sret argument |
| // into %D0 for the return. Save the argument into a virtual register so |
| // that we can access it from the return points. |
| if (Ins[i].Flags.isSRet()) { |
| unsigned Reg = MMFI->getSRetReturnReg(); |
| if (!Reg) { |
| MVT PtrTy = getPointerTy(DAG.getDataLayout()); |
| Reg = MF.getRegInfo().createVirtualRegister(getRegClassFor(PtrTy)); |
| MMFI->setSRetReturnReg(Reg); |
| } |
| SDValue Copy = DAG.getCopyToReg(DAG.getEntryNode(), DL, Reg, InVals[i]); |
| Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Copy, Chain); |
| break; |
| } |
| } |
| |
| unsigned StackSize = CCInfo.getStackSize(); |
| // Align stack specially for tail calls. |
| if (shouldGuaranteeTCO(CCID, MF.getTarget().Options.GuaranteedTailCallOpt)) |
| StackSize = GetAlignedArgumentStackSize(StackSize, DAG); |
| |
| // If the function takes variable number of arguments, make a frame index for |
| // the start of the first vararg value... for expansion of llvm.va_start. We |
| // can skip this if there are no va_start calls. |
| if (MFI.hasVAStart()) { |
| MMFI->setVarArgsFrameIndex(MFI.CreateFixedObject(1, StackSize, true)); |
| } |
| |
| if (IsVarArg && MFI.hasMustTailInVarArgFunc()) { |
| // We forward some GPRs and some vector types. |
| SmallVector<MVT, 2> RegParmTypes; |
| MVT IntVT = MVT::i32; |
| RegParmTypes.push_back(IntVT); |
| |
| // Compute the set of forwarded registers. The rest are scratch. |
| // ??? what is this for? |
| SmallVectorImpl<ForwardedRegister> &Forwards = |
| MMFI->getForwardedMustTailRegParms(); |
| CCInfo.analyzeMustTailForwardedRegisters(Forwards, RegParmTypes, CC_M68k); |
| |
| // Copy all forwards from physical to virtual registers. |
| for (ForwardedRegister &F : Forwards) { |
| // FIXME Can we use a less constrained schedule? |
| SDValue RegVal = DAG.getCopyFromReg(Chain, DL, F.VReg, F.VT); |
| F.VReg = MF.getRegInfo().createVirtualRegister(getRegClassFor(F.VT)); |
| Chain = DAG.getCopyToReg(Chain, DL, F.VReg, RegVal); |
| } |
| } |
| |
| // Some CCs need callee pop. |
| if (M68k::isCalleePop(CCID, IsVarArg, |
| MF.getTarget().Options.GuaranteedTailCallOpt)) { |
| MMFI->setBytesToPopOnReturn(StackSize); // Callee pops everything. |
| } else { |
| MMFI->setBytesToPopOnReturn(0); // Callee pops nothing. |
| // If this is an sret function, the return should pop the hidden pointer. |
| if (!canGuaranteeTCO(CCID) && argsAreStructReturn(Ins) == StackStructReturn) |
| MMFI->setBytesToPopOnReturn(4); |
| } |
| |
| MMFI->setArgumentStackSize(StackSize); |
| |
| return Chain; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Return Value Calling Convention Implementation |
| //===----------------------------------------------------------------------===// |
| |
| bool M68kTargetLowering::CanLowerReturn( |
| CallingConv::ID CCID, MachineFunction &MF, bool IsVarArg, |
| const SmallVectorImpl<ISD::OutputArg> &Outs, LLVMContext &Context) const { |
| SmallVector<CCValAssign, 16> RVLocs; |
| CCState CCInfo(CCID, IsVarArg, MF, RVLocs, Context); |
| return CCInfo.CheckReturn(Outs, RetCC_M68k); |
| } |
| |
| SDValue |
| M68kTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CCID, |
| bool IsVarArg, |
| const SmallVectorImpl<ISD::OutputArg> &Outs, |
| const SmallVectorImpl<SDValue> &OutVals, |
| const SDLoc &DL, SelectionDAG &DAG) const { |
| MachineFunction &MF = DAG.getMachineFunction(); |
| M68kMachineFunctionInfo *MFI = MF.getInfo<M68kMachineFunctionInfo>(); |
| |
| SmallVector<CCValAssign, 16> RVLocs; |
| CCState CCInfo(CCID, IsVarArg, MF, RVLocs, *DAG.getContext()); |
| CCInfo.AnalyzeReturn(Outs, RetCC_M68k); |
| |
| SDValue Glue; |
| SmallVector<SDValue, 6> RetOps; |
| // Operand #0 = Chain (updated below) |
| RetOps.push_back(Chain); |
| // Operand #1 = Bytes To Pop |
| RetOps.push_back( |
| DAG.getTargetConstant(MFI->getBytesToPopOnReturn(), DL, MVT::i32)); |
| |
| // Copy the result values into the output registers. |
| for (unsigned i = 0, e = RVLocs.size(); i != e; ++i) { |
| CCValAssign &VA = RVLocs[i]; |
| assert(VA.isRegLoc() && "Can only return in registers!"); |
| SDValue ValToCopy = OutVals[i]; |
| EVT ValVT = ValToCopy.getValueType(); |
| |
| // Promote values to the appropriate types. |
| if (VA.getLocInfo() == CCValAssign::SExt) |
| ValToCopy = DAG.getNode(ISD::SIGN_EXTEND, DL, VA.getLocVT(), ValToCopy); |
| else if (VA.getLocInfo() == CCValAssign::ZExt) |
| ValToCopy = DAG.getNode(ISD::ZERO_EXTEND, DL, VA.getLocVT(), ValToCopy); |
| else if (VA.getLocInfo() == CCValAssign::AExt) { |
| if (ValVT.isVector() && ValVT.getVectorElementType() == MVT::i1) |
| ValToCopy = DAG.getNode(ISD::SIGN_EXTEND, DL, VA.getLocVT(), ValToCopy); |
| else |
| ValToCopy = DAG.getNode(ISD::ANY_EXTEND, DL, VA.getLocVT(), ValToCopy); |
| } else if (VA.getLocInfo() == CCValAssign::BCvt) |
| ValToCopy = DAG.getBitcast(VA.getLocVT(), ValToCopy); |
| |
| Chain = DAG.getCopyToReg(Chain, DL, VA.getLocReg(), ValToCopy, Glue); |
| Glue = Chain.getValue(1); |
| RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT())); |
| } |
| |
| // Swift calling convention does not require we copy the sret argument |
| // into %d0 for the return, and SRetReturnReg is not set for Swift. |
| |
| // ABI require that for returning structs by value we copy the sret argument |
| // into %D0 for the return. Save the argument into a virtual register so that |
| // we can access it from the return points. |
| // |
| // Checking Function.hasStructRetAttr() here is insufficient because the IR |
| // may not have an explicit sret argument. If MFI.CanLowerReturn is |
| // false, then an sret argument may be implicitly inserted in the SelDAG. In |
| // either case MFI->setSRetReturnReg() will have been called. |
| if (unsigned SRetReg = MFI->getSRetReturnReg()) { |
| // ??? Can i just move this to the top and escape this explanation? |
| // When we have both sret and another return value, we should use the |
| // original Chain stored in RetOps[0], instead of the current Chain updated |
| // in the above loop. If we only have sret, RetOps[0] equals to Chain. |
| |
| // For the case of sret and another return value, we have |
| // Chain_0 at the function entry |
| // Chain_1 = getCopyToReg(Chain_0) in the above loop |
| // If we use Chain_1 in getCopyFromReg, we will have |
| // Val = getCopyFromReg(Chain_1) |
| // Chain_2 = getCopyToReg(Chain_1, Val) from below |
| |
| // getCopyToReg(Chain_0) will be glued together with |
| // getCopyToReg(Chain_1, Val) into Unit A, getCopyFromReg(Chain_1) will be |
| // in Unit B, and we will have cyclic dependency between Unit A and Unit B: |
| // Data dependency from Unit B to Unit A due to usage of Val in |
| // getCopyToReg(Chain_1, Val) |
| // Chain dependency from Unit A to Unit B |
| |
| // So here, we use RetOps[0] (i.e Chain_0) for getCopyFromReg. |
| SDValue Val = DAG.getCopyFromReg(RetOps[0], DL, SRetReg, |
| getPointerTy(MF.getDataLayout())); |
| |
| // ??? How will this work if CC does not use registers for args passing? |
| // ??? What if I return multiple structs? |
| unsigned RetValReg = M68k::D0; |
| Chain = DAG.getCopyToReg(Chain, DL, RetValReg, Val, Glue); |
| Glue = Chain.getValue(1); |
| |
| RetOps.push_back( |
| DAG.getRegister(RetValReg, getPointerTy(DAG.getDataLayout()))); |
| } |
| |
| RetOps[0] = Chain; // Update chain. |
| |
| // Add the glue if we have it. |
| if (Glue.getNode()) |
| RetOps.push_back(Glue); |
| |
| return DAG.getNode(M68kISD::RET, DL, MVT::Other, RetOps); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Fast Calling Convention (tail call) implementation |
| //===----------------------------------------------------------------------===// |
| |
| // Like std call, callee cleans arguments, convention except that ECX is |
| // reserved for storing the tail called function address. Only 2 registers are |
| // free for argument passing (inreg). Tail call optimization is performed |
| // provided: |
| // * tailcallopt is enabled |
| // * caller/callee are fastcc |
| // On M68k_64 architecture with GOT-style position independent code only |
| // local (within module) calls are supported at the moment. To keep the stack |
| // aligned according to platform abi the function GetAlignedArgumentStackSize |
| // ensures that argument delta is always multiples of stack alignment. (Dynamic |
| // linkers need this - darwin's dyld for example) If a tail called function |
| // callee has more arguments than the caller the caller needs to make sure that |
| // there is room to move the RETADDR to. This is achieved by reserving an area |
| // the size of the argument delta right after the original RETADDR, but before |
| // the saved framepointer or the spilled registers e.g. caller(arg1, arg2) |
| // calls callee(arg1, arg2,arg3,arg4) stack layout: |
| // arg1 |
| // arg2 |
| // RETADDR |
| // [ new RETADDR |
| // move area ] |
| // (possible EBP) |
| // ESI |
| // EDI |
| // local1 .. |
| |
| /// Make the stack size align e.g 16n + 12 aligned for a 16-byte align |
| /// requirement. |
| unsigned |
| M68kTargetLowering::GetAlignedArgumentStackSize(unsigned StackSize, |
| SelectionDAG &DAG) const { |
| const TargetFrameLowering &TFI = *Subtarget.getFrameLowering(); |
| unsigned StackAlignment = TFI.getStackAlignment(); |
| uint64_t AlignMask = StackAlignment - 1; |
| int64_t Offset = StackSize; |
| unsigned SlotSize = Subtarget.getSlotSize(); |
| if ((Offset & AlignMask) <= (StackAlignment - SlotSize)) { |
| // Number smaller than 12 so just add the difference. |
| Offset += ((StackAlignment - SlotSize) - (Offset & AlignMask)); |
| } else { |
| // Mask out lower bits, add stackalignment once plus the 12 bytes. |
| Offset = |
| ((~AlignMask) & Offset) + StackAlignment + (StackAlignment - SlotSize); |
| } |
| return Offset; |
| } |
| |
| /// Check whether the call is eligible for tail call optimization. Targets |
| /// that want to do tail call optimization should implement this function. |
| bool M68kTargetLowering::IsEligibleForTailCallOptimization( |
| SDValue Callee, CallingConv::ID CalleeCC, bool IsVarArg, |
| bool IsCalleeStructRet, bool IsCallerStructRet, Type *RetTy, |
| const SmallVectorImpl<ISD::OutputArg> &Outs, |
| const SmallVectorImpl<SDValue> &OutVals, |
| const SmallVectorImpl<ISD::InputArg> &Ins, SelectionDAG &DAG) const { |
| if (!mayTailCallThisCC(CalleeCC)) |
| return false; |
| |
| // If -tailcallopt is specified, make fastcc functions tail-callable. |
| MachineFunction &MF = DAG.getMachineFunction(); |
| const auto &CallerF = MF.getFunction(); |
| |
| CallingConv::ID CallerCC = CallerF.getCallingConv(); |
| bool CCMatch = CallerCC == CalleeCC; |
| |
| if (DAG.getTarget().Options.GuaranteedTailCallOpt) { |
| if (canGuaranteeTCO(CalleeCC) && CCMatch) |
| return true; |
| return false; |
| } |
| |
| // Look for obvious safe cases to perform tail call optimization that do not |
| // require ABI changes. This is what gcc calls sibcall. |
| |
| // Can't do sibcall if stack needs to be dynamically re-aligned. PEI needs to |
| // emit a special epilogue. |
| const M68kRegisterInfo *RegInfo = Subtarget.getRegisterInfo(); |
| if (RegInfo->hasStackRealignment(MF)) |
| return false; |
| |
| // Also avoid sibcall optimization if either caller or callee uses struct |
| // return semantics. |
| if (IsCalleeStructRet || IsCallerStructRet) |
| return false; |
| |
| // Do not sibcall optimize vararg calls unless all arguments are passed via |
| // registers. |
| LLVMContext &C = *DAG.getContext(); |
| if (IsVarArg && !Outs.empty()) { |
| |
| SmallVector<CCValAssign, 16> ArgLocs; |
| CCState CCInfo(CalleeCC, IsVarArg, MF, ArgLocs, C); |
| |
| CCInfo.AnalyzeCallOperands(Outs, CC_M68k); |
| for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) |
| if (!ArgLocs[i].isRegLoc()) |
| return false; |
| } |
| |
| // Check that the call results are passed in the same way. |
| if (!CCState::resultsCompatible(CalleeCC, CallerCC, MF, C, Ins, RetCC_M68k, |
| RetCC_M68k)) |
| return false; |
| |
| // The callee has to preserve all registers the caller needs to preserve. |
| const M68kRegisterInfo *TRI = Subtarget.getRegisterInfo(); |
| const uint32_t *CallerPreserved = TRI->getCallPreservedMask(MF, CallerCC); |
| if (!CCMatch) { |
| const uint32_t *CalleePreserved = TRI->getCallPreservedMask(MF, CalleeCC); |
| if (!TRI->regmaskSubsetEqual(CallerPreserved, CalleePreserved)) |
| return false; |
| } |
| |
| unsigned StackArgsSize = 0; |
| |
| // If the callee takes no arguments then go on to check the results of the |
| // call. |
| if (!Outs.empty()) { |
| // Check if stack adjustment is needed. For now, do not do this if any |
| // argument is passed on the stack. |
| SmallVector<CCValAssign, 16> ArgLocs; |
| CCState CCInfo(CalleeCC, IsVarArg, MF, ArgLocs, C); |
| |
| CCInfo.AnalyzeCallOperands(Outs, CC_M68k); |
| StackArgsSize = CCInfo.getStackSize(); |
| |
| if (StackArgsSize) { |
| // Check if the arguments are already laid out in the right way as |
| // the caller's fixed stack objects. |
| MachineFrameInfo &MFI = MF.getFrameInfo(); |
| const MachineRegisterInfo *MRI = &MF.getRegInfo(); |
| const M68kInstrInfo *TII = Subtarget.getInstrInfo(); |
| for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { |
| CCValAssign &VA = ArgLocs[i]; |
| SDValue Arg = OutVals[i]; |
| ISD::ArgFlagsTy Flags = Outs[i].Flags; |
| if (VA.getLocInfo() == CCValAssign::Indirect) |
| return false; |
| if (!VA.isRegLoc()) { |
| if (!MatchingStackOffset(Arg, VA.getLocMemOffset(), Flags, MFI, MRI, |
| TII, VA)) |
| return false; |
| } |
| } |
| } |
| |
| bool PositionIndependent = isPositionIndependent(); |
| // If the tailcall address may be in a register, then make sure it's |
| // possible to register allocate for it. The call address can |
| // only target %A0 or %A1 since the tail call must be scheduled after |
| // callee-saved registers are restored. These happen to be the same |
| // registers used to pass 'inreg' arguments so watch out for those. |
| if ((!isa<GlobalAddressSDNode>(Callee) && |
| !isa<ExternalSymbolSDNode>(Callee)) || |
| PositionIndependent) { |
| unsigned NumInRegs = 0; |
| // In PIC we need an extra register to formulate the address computation |
| // for the callee. |
| unsigned MaxInRegs = PositionIndependent ? 1 : 2; |
| |
| for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { |
| CCValAssign &VA = ArgLocs[i]; |
| if (!VA.isRegLoc()) |
| continue; |
| Register Reg = VA.getLocReg(); |
| switch (Reg) { |
| default: |
| break; |
| case M68k::A0: |
| case M68k::A1: |
| if (++NumInRegs == MaxInRegs) |
| return false; |
| break; |
| } |
| } |
| } |
| |
| const MachineRegisterInfo &MRI = MF.getRegInfo(); |
| if (!parametersInCSRMatch(MRI, CallerPreserved, ArgLocs, OutVals)) |
| return false; |
| } |
| |
| bool CalleeWillPop = M68k::isCalleePop( |
| CalleeCC, IsVarArg, MF.getTarget().Options.GuaranteedTailCallOpt); |
| |
| if (unsigned BytesToPop = |
| MF.getInfo<M68kMachineFunctionInfo>()->getBytesToPopOnReturn()) { |
| // If we have bytes to pop, the callee must pop them. |
| bool CalleePopMatches = CalleeWillPop && BytesToPop == StackArgsSize; |
| if (!CalleePopMatches) |
| return false; |
| } else if (CalleeWillPop && StackArgsSize > 0) { |
| // If we don't have bytes to pop, make sure the callee doesn't pop any. |
| return false; |
| } |
| |
| return true; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Custom Lower |
| //===----------------------------------------------------------------------===// |
| |
| SDValue M68kTargetLowering::LowerOperation(SDValue Op, |
| SelectionDAG &DAG) const { |
| switch (Op.getOpcode()) { |
| default: |
| llvm_unreachable("Should not custom lower this!"); |
| case ISD::SADDO: |
| case ISD::UADDO: |
| case ISD::SSUBO: |
| case ISD::USUBO: |
| case ISD::SMULO: |
| case ISD::UMULO: |
| return LowerXALUO(Op, DAG); |
| case ISD::SETCC: |
| return LowerSETCC(Op, DAG); |
| case ISD::SETCCCARRY: |
| return LowerSETCCCARRY(Op, DAG); |
| case ISD::SELECT: |
| return LowerSELECT(Op, DAG); |
| case ISD::BRCOND: |
| return LowerBRCOND(Op, DAG); |
| case ISD::ADDC: |
| case ISD::ADDE: |
| case ISD::SUBC: |
| case ISD::SUBE: |
| return LowerADDC_ADDE_SUBC_SUBE(Op, DAG); |
| case ISD::ConstantPool: |
| return LowerConstantPool(Op, DAG); |
| case ISD::GlobalAddress: |
| return LowerGlobalAddress(Op, DAG); |
| case ISD::ExternalSymbol: |
| return LowerExternalSymbol(Op, DAG); |
| case ISD::BlockAddress: |
| return LowerBlockAddress(Op, DAG); |
| case ISD::JumpTable: |
| return LowerJumpTable(Op, DAG); |
| case ISD::VASTART: |
| return LowerVASTART(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); |
| case ISD::ATOMIC_FENCE: |
| return LowerATOMICFENCE(Op, DAG); |
| case ISD::GlobalTLSAddress: |
| return LowerGlobalTLSAddress(Op, DAG); |
| } |
| } |
| |
| SDValue M68kTargetLowering::LowerExternalSymbolCall(SelectionDAG &DAG, |
| SDLoc Loc, |
| llvm::StringRef SymbolName, |
| ArgListTy &&ArgList) const { |
| PointerType *PtrTy = PointerType::get(*DAG.getContext(), 0); |
| CallLoweringInfo CLI(DAG); |
| CLI.setDebugLoc(Loc) |
| .setChain(DAG.getEntryNode()) |
| .setLibCallee(CallingConv::C, PtrTy, |
| DAG.getExternalSymbol(SymbolName.data(), |
| getPointerMemTy(DAG.getDataLayout())), |
| std::move(ArgList)); |
| return LowerCallTo(CLI).first; |
| } |
| |
| SDValue M68kTargetLowering::getTLSGetAddr(GlobalAddressSDNode *GA, |
| SelectionDAG &DAG, |
| unsigned TargetFlags) const { |
| SDValue GOT = DAG.getGLOBAL_OFFSET_TABLE(MVT::i32); |
| SDValue TGA = DAG.getTargetGlobalAddress( |
| GA->getGlobal(), GA, GA->getValueType(0), GA->getOffset(), TargetFlags); |
| SDValue Arg = DAG.getNode(ISD::ADD, SDLoc(GA), MVT::i32, GOT, TGA); |
| |
| PointerType *PtrTy = PointerType::get(*DAG.getContext(), 0); |
| |
| ArgListTy Args; |
| ArgListEntry Entry; |
| Entry.Node = Arg; |
| Entry.Ty = PtrTy; |
| Args.push_back(Entry); |
| return LowerExternalSymbolCall(DAG, SDLoc(GA), "__tls_get_addr", |
| std::move(Args)); |
| } |
| |
| SDValue M68kTargetLowering::getM68kReadTp(SDLoc Loc, SelectionDAG &DAG) const { |
| return LowerExternalSymbolCall(DAG, Loc, "__m68k_read_tp", ArgListTy()); |
| } |
| |
| SDValue M68kTargetLowering::LowerTLSGeneralDynamic(GlobalAddressSDNode *GA, |
| SelectionDAG &DAG) const { |
| return getTLSGetAddr(GA, DAG, M68kII::MO_TLSGD); |
| } |
| |
| SDValue M68kTargetLowering::LowerTLSLocalDynamic(GlobalAddressSDNode *GA, |
| SelectionDAG &DAG) const { |
| SDValue Addr = getTLSGetAddr(GA, DAG, M68kII::MO_TLSLDM); |
| SDValue TGA = |
| DAG.getTargetGlobalAddress(GA->getGlobal(), GA, GA->getValueType(0), |
| GA->getOffset(), M68kII::MO_TLSLD); |
| return DAG.getNode(ISD::ADD, SDLoc(GA), MVT::i32, TGA, Addr); |
| } |
| |
| SDValue M68kTargetLowering::LowerTLSInitialExec(GlobalAddressSDNode *GA, |
| SelectionDAG &DAG) const { |
| SDValue GOT = DAG.getGLOBAL_OFFSET_TABLE(MVT::i32); |
| SDValue Tp = getM68kReadTp(SDLoc(GA), DAG); |
| SDValue TGA = |
| DAG.getTargetGlobalAddress(GA->getGlobal(), GA, GA->getValueType(0), |
| GA->getOffset(), M68kII::MO_TLSIE); |
| SDValue Addr = DAG.getNode(ISD::ADD, SDLoc(GA), MVT::i32, TGA, GOT); |
| SDValue Offset = |
| DAG.getLoad(MVT::i32, SDLoc(GA), DAG.getEntryNode(), Addr, |
| MachinePointerInfo::getGOT(DAG.getMachineFunction())); |
| |
| return DAG.getNode(ISD::ADD, SDLoc(GA), MVT::i32, Offset, Tp); |
| } |
| |
| SDValue M68kTargetLowering::LowerTLSLocalExec(GlobalAddressSDNode *GA, |
| SelectionDAG &DAG) const { |
| SDValue Tp = getM68kReadTp(SDLoc(GA), DAG); |
| SDValue TGA = |
| DAG.getTargetGlobalAddress(GA->getGlobal(), GA, GA->getValueType(0), |
| GA->getOffset(), M68kII::MO_TLSLE); |
| return DAG.getNode(ISD::ADD, SDLoc(GA), MVT::i32, TGA, Tp); |
| } |
| |
| SDValue M68kTargetLowering::LowerGlobalTLSAddress(SDValue Op, |
| SelectionDAG &DAG) const { |
| assert(Subtarget.isTargetELF()); |
| |
| auto *GA = cast<GlobalAddressSDNode>(Op); |
| TLSModel::Model AccessModel = DAG.getTarget().getTLSModel(GA->getGlobal()); |
| |
| switch (AccessModel) { |
| case TLSModel::GeneralDynamic: |
| return LowerTLSGeneralDynamic(GA, DAG); |
| case TLSModel::LocalDynamic: |
| return LowerTLSLocalDynamic(GA, DAG); |
| case TLSModel::InitialExec: |
| return LowerTLSInitialExec(GA, DAG); |
| case TLSModel::LocalExec: |
| return LowerTLSLocalExec(GA, DAG); |
| } |
| |
| llvm_unreachable("Unexpected TLS access model type"); |
| } |
| |
| bool M68kTargetLowering::decomposeMulByConstant(LLVMContext &Context, EVT VT, |
| SDValue C) const { |
| // Shifts and add instructions in M68000 and M68010 support |
| // up to 32 bits, but mul only has 16-bit variant. So it's almost |
| // certainly beneficial to lower 8/16/32-bit mul to their |
| // add / shifts counterparts. But for 64-bits mul, it might be |
| // safer to just leave it to compiler runtime implementations. |
| return VT.bitsLE(MVT::i32) || Subtarget.atLeastM68020(); |
| } |
| |
| static bool isOverflowArithmetic(unsigned Opcode) { |
| switch (Opcode) { |
| case ISD::UADDO: |
| case ISD::SADDO: |
| case ISD::USUBO: |
| case ISD::SSUBO: |
| case ISD::UMULO: |
| case ISD::SMULO: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static void lowerOverflowArithmetic(SDValue Op, SelectionDAG &DAG, |
| SDValue &Result, SDValue &CCR, |
| unsigned &CC) { |
| SDNode *N = Op.getNode(); |
| EVT VT = N->getValueType(0); |
| SDValue LHS = N->getOperand(0); |
| SDValue RHS = N->getOperand(1); |
| SDLoc DL(Op); |
| |
| unsigned TruncOp = 0; |
| auto PromoteMULO = [&](unsigned ExtOp) { |
| // We don't have 8-bit multiplications, so promote i8 version of U/SMULO |
| // to i16. |
| // Ideally this should be done by legalizer but sadly there is no promotion |
| // rule for U/SMULO at this moment. |
| if (VT == MVT::i8) { |
| LHS = DAG.getNode(ExtOp, DL, MVT::i16, LHS); |
| RHS = DAG.getNode(ExtOp, DL, MVT::i16, RHS); |
| VT = MVT::i16; |
| TruncOp = ISD::TRUNCATE; |
| } |
| }; |
| |
| bool NoOverflow = false; |
| unsigned BaseOp = 0; |
| switch (Op.getOpcode()) { |
| default: |
| llvm_unreachable("Unknown ovf instruction!"); |
| case ISD::SADDO: |
| BaseOp = M68kISD::ADD; |
| CC = M68k::COND_VS; |
| break; |
| case ISD::UADDO: |
| BaseOp = M68kISD::ADD; |
| CC = M68k::COND_CS; |
| break; |
| case ISD::SSUBO: |
| BaseOp = M68kISD::SUB; |
| CC = M68k::COND_VS; |
| break; |
| case ISD::USUBO: |
| BaseOp = M68kISD::SUB; |
| CC = M68k::COND_CS; |
| break; |
| case ISD::UMULO: |
| PromoteMULO(ISD::ZERO_EXTEND); |
| NoOverflow = VT != MVT::i32; |
| BaseOp = NoOverflow ? ISD::MUL : M68kISD::UMUL; |
| CC = M68k::COND_VS; |
| break; |
| case ISD::SMULO: |
| PromoteMULO(ISD::SIGN_EXTEND); |
| NoOverflow = VT != MVT::i32; |
| BaseOp = NoOverflow ? ISD::MUL : M68kISD::SMUL; |
| CC = M68k::COND_VS; |
| break; |
| } |
| |
| SDVTList VTs; |
| if (NoOverflow) |
| VTs = DAG.getVTList(VT); |
| else |
| // Also sets CCR. |
| VTs = DAG.getVTList(VT, MVT::i8); |
| |
| SDValue Arith = DAG.getNode(BaseOp, DL, VTs, LHS, RHS); |
| Result = Arith.getValue(0); |
| if (TruncOp) |
| // Right now the only place to truncate is from i16 to i8. |
| Result = DAG.getNode(TruncOp, DL, MVT::i8, Arith); |
| |
| if (NoOverflow) |
| CCR = DAG.getConstant(0, DL, N->getValueType(1)); |
| else |
| CCR = Arith.getValue(1); |
| } |
| |
| SDValue M68kTargetLowering::LowerXALUO(SDValue Op, SelectionDAG &DAG) const { |
| SDNode *N = Op.getNode(); |
| SDLoc DL(Op); |
| |
| // Lower the "add/sub/mul with overflow" instruction into a regular ins plus |
| // a "setcc" instruction that checks the overflow flag. |
| SDValue Result, CCR; |
| unsigned CC; |
| lowerOverflowArithmetic(Op, DAG, Result, CCR, CC); |
| |
| SDValue Overflow; |
| if (isa<ConstantSDNode>(CCR)) { |
| // It's likely a result of operations that will not overflow |
| // hence no setcc is needed. |
| Overflow = CCR; |
| } else { |
| // Generate a M68kISD::SETCC. |
| Overflow = DAG.getNode(M68kISD::SETCC, DL, N->getValueType(1), |
| DAG.getConstant(CC, DL, MVT::i8), CCR); |
| } |
| |
| return DAG.getNode(ISD::MERGE_VALUES, DL, N->getVTList(), Result, Overflow); |
| } |
| |
| /// Create a BTST (Bit Test) node - Test bit \p BitNo in \p Src and set |
| /// condition according to equal/not-equal condition code \p CC. |
| static SDValue getBitTestCondition(SDValue Src, SDValue BitNo, ISD::CondCode CC, |
| const SDLoc &DL, SelectionDAG &DAG) { |
| // If Src is i8, promote it to i32 with any_extend. There is no i8 BTST |
| // instruction. Since the shift amount is in-range-or-undefined, we know |
| // that doing a bittest on the i32 value is ok. |
| if (Src.getValueType() == MVT::i8 || Src.getValueType() == MVT::i16) |
| Src = DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i32, Src); |
| |
| // If the operand types disagree, extend the shift amount to match. Since |
| // BTST ignores high bits (like shifts) we can use anyextend. |
| if (Src.getValueType() != BitNo.getValueType()) |
| BitNo = DAG.getNode(ISD::ANY_EXTEND, DL, Src.getValueType(), BitNo); |
| |
| SDValue BTST = DAG.getNode(M68kISD::BTST, DL, MVT::i32, Src, BitNo); |
| |
| // NOTE BTST sets CCR.Z flag |
| M68k::CondCode Cond = CC == ISD::SETEQ ? M68k::COND_NE : M68k::COND_EQ; |
| return DAG.getNode(M68kISD::SETCC, DL, MVT::i8, |
| DAG.getConstant(Cond, DL, MVT::i8), BTST); |
| } |
| |
| /// Result of 'and' is compared against zero. Change to a BTST node if possible. |
| static SDValue LowerAndToBTST(SDValue And, ISD::CondCode CC, const SDLoc &DL, |
| SelectionDAG &DAG) { |
| SDValue Op0 = And.getOperand(0); |
| SDValue Op1 = And.getOperand(1); |
| if (Op0.getOpcode() == ISD::TRUNCATE) |
| Op0 = Op0.getOperand(0); |
| if (Op1.getOpcode() == ISD::TRUNCATE) |
| Op1 = Op1.getOperand(0); |
| |
| SDValue LHS, RHS; |
| if (Op1.getOpcode() == ISD::SHL) |
| std::swap(Op0, Op1); |
| if (Op0.getOpcode() == ISD::SHL) { |
| if (isOneConstant(Op0.getOperand(0))) { |
| // If we looked past a truncate, check that it's only truncating away |
| // known zeros. |
| unsigned BitWidth = Op0.getValueSizeInBits(); |
| unsigned AndBitWidth = And.getValueSizeInBits(); |
| if (BitWidth > AndBitWidth) { |
| auto Known = DAG.computeKnownBits(Op0); |
| if (Known.countMinLeadingZeros() < BitWidth - AndBitWidth) |
| return SDValue(); |
| } |
| LHS = Op1; |
| RHS = Op0.getOperand(1); |
| } |
| } else if (auto *AndRHS = dyn_cast<ConstantSDNode>(Op1)) { |
| uint64_t AndRHSVal = AndRHS->getZExtValue(); |
| SDValue AndLHS = Op0; |
| |
| if (AndRHSVal == 1 && AndLHS.getOpcode() == ISD::SRL) { |
| LHS = AndLHS.getOperand(0); |
| RHS = AndLHS.getOperand(1); |
| } |
| |
| // Use BTST if the immediate can't be encoded in a TEST instruction. |
| if (!isUInt<32>(AndRHSVal) && isPowerOf2_64(AndRHSVal)) { |
| LHS = AndLHS; |
| RHS = DAG.getConstant(Log2_64_Ceil(AndRHSVal), DL, LHS.getValueType()); |
| } |
| } |
| |
| if (LHS.getNode()) |
| return getBitTestCondition(LHS, RHS, CC, DL, DAG); |
| |
| return SDValue(); |
| } |
| |
| static M68k::CondCode TranslateIntegerM68kCC(ISD::CondCode SetCCOpcode) { |
| switch (SetCCOpcode) { |
| default: |
| llvm_unreachable("Invalid integer condition!"); |
| case ISD::SETEQ: |
| return M68k::COND_EQ; |
| case ISD::SETGT: |
| return M68k::COND_GT; |
| case ISD::SETGE: |
| return M68k::COND_GE; |
| case ISD::SETLT: |
| return M68k::COND_LT; |
| case ISD::SETLE: |
| return M68k::COND_LE; |
| case ISD::SETNE: |
| return M68k::COND_NE; |
| case ISD::SETULT: |
| return M68k::COND_CS; |
| case ISD::SETUGE: |
| return M68k::COND_CC; |
| case ISD::SETUGT: |
| return M68k::COND_HI; |
| case ISD::SETULE: |
| return M68k::COND_LS; |
| } |
| } |
| |
| /// Do a one-to-one translation of a ISD::CondCode to the M68k-specific |
| /// condition code, returning the condition code and the LHS/RHS of the |
| /// comparison to make. |
| static unsigned TranslateM68kCC(ISD::CondCode SetCCOpcode, const SDLoc &DL, |
| bool IsFP, SDValue &LHS, SDValue &RHS, |
| SelectionDAG &DAG) { |
| if (!IsFP) { |
| if (ConstantSDNode *RHSC = dyn_cast<ConstantSDNode>(RHS)) { |
| if (SetCCOpcode == ISD::SETGT && RHSC->isAllOnes()) { |
| // X > -1 -> X == 0, jump !sign. |
| RHS = DAG.getConstant(0, DL, RHS.getValueType()); |
| return M68k::COND_PL; |
| } |
| if (SetCCOpcode == ISD::SETLT && RHSC->isZero()) { |
| // X < 0 -> X == 0, jump on sign. |
| return M68k::COND_MI; |
| } |
| if (SetCCOpcode == ISD::SETLT && RHSC->getZExtValue() == 1) { |
| // X < 1 -> X <= 0 |
| RHS = DAG.getConstant(0, DL, RHS.getValueType()); |
| return M68k::COND_LE; |
| } |
| } |
| |
| return TranslateIntegerM68kCC(SetCCOpcode); |
| } |
| |
| // First determine if it is required or is profitable to flip the operands. |
| |
| // If LHS is a foldable load, but RHS is not, flip the condition. |
| if (ISD::isNON_EXTLoad(LHS.getNode()) && !ISD::isNON_EXTLoad(RHS.getNode())) { |
| SetCCOpcode = getSetCCSwappedOperands(SetCCOpcode); |
| std::swap(LHS, RHS); |
| } |
| |
| switch (SetCCOpcode) { |
| default: |
| break; |
| case ISD::SETOLT: |
| case ISD::SETOLE: |
| case ISD::SETUGT: |
| case ISD::SETUGE: |
| std::swap(LHS, RHS); |
| break; |
| } |
| |
| // On a floating point condition, the flags are set as follows: |
| // ZF PF CF op |
| // 0 | 0 | 0 | X > Y |
| // 0 | 0 | 1 | X < Y |
| // 1 | 0 | 0 | X == Y |
| // 1 | 1 | 1 | unordered |
| switch (SetCCOpcode) { |
| default: |
| llvm_unreachable("Condcode should be pre-legalized away"); |
| case ISD::SETUEQ: |
| case ISD::SETEQ: |
| return M68k::COND_EQ; |
| case ISD::SETOLT: // flipped |
| case ISD::SETOGT: |
| case ISD::SETGT: |
| return M68k::COND_HI; |
| case ISD::SETOLE: // flipped |
| case ISD::SETOGE: |
| case ISD::SETGE: |
| return M68k::COND_CC; |
| case ISD::SETUGT: // flipped |
| case ISD::SETULT: |
| case ISD::SETLT: |
| return M68k::COND_CS; |
| case ISD::SETUGE: // flipped |
| case ISD::SETULE: |
| case ISD::SETLE: |
| return M68k::COND_LS; |
| case ISD::SETONE: |
| case ISD::SETNE: |
| return M68k::COND_NE; |
| case ISD::SETOEQ: |
| case ISD::SETUNE: |
| return M68k::COND_INVALID; |
| } |
| } |
| |
| // Convert (truncate (srl X, N) to i1) to (bt X, N) |
| static SDValue LowerTruncateToBTST(SDValue Op, ISD::CondCode CC, |
| const SDLoc &DL, SelectionDAG &DAG) { |
| |
| assert(Op.getOpcode() == ISD::TRUNCATE && Op.getValueType() == MVT::i1 && |
| "Expected TRUNCATE to i1 node"); |
| |
| if (Op.getOperand(0).getOpcode() != ISD::SRL) |
| return SDValue(); |
| |
| SDValue ShiftRight = Op.getOperand(0); |
| return getBitTestCondition(ShiftRight.getOperand(0), ShiftRight.getOperand(1), |
| CC, DL, DAG); |
| } |
| |
| /// \brief return true if \c Op has a use that doesn't just read flags. |
| static bool hasNonFlagsUse(SDValue Op) { |
| for (SDNode::use_iterator UI = Op->use_begin(), UE = Op->use_end(); UI != UE; |
| ++UI) { |
| SDNode *User = *UI; |
| unsigned UOpNo = UI.getOperandNo(); |
| if (User->getOpcode() == ISD::TRUNCATE && User->hasOneUse()) { |
| // Look pass truncate. |
| UOpNo = User->use_begin().getOperandNo(); |
| User = *User->use_begin(); |
| } |
| |
| if (User->getOpcode() != ISD::BRCOND && User->getOpcode() != ISD::SETCC && |
| !(User->getOpcode() == ISD::SELECT && UOpNo == 0)) |
| return true; |
| } |
| return false; |
| } |
| |
| SDValue M68kTargetLowering::EmitTest(SDValue Op, unsigned M68kCC, |
| const SDLoc &DL, SelectionDAG &DAG) const { |
| |
| // CF and OF aren't always set the way we want. Determine which |
| // of these we need. |
| bool NeedCF = false; |
| bool NeedOF = false; |
| switch (M68kCC) { |
| default: |
| break; |
| case M68k::COND_HI: |
| case M68k::COND_CC: |
| case M68k::COND_CS: |
| case M68k::COND_LS: |
| NeedCF = true; |
| break; |
| case M68k::COND_GT: |
| case M68k::COND_GE: |
| case M68k::COND_LT: |
| case M68k::COND_LE: |
| case M68k::COND_VS: |
| case M68k::COND_VC: { |
| // Check if we really need to set the |
| // Overflow flag. If NoSignedWrap is present |
| // that is not actually needed. |
| switch (Op->getOpcode()) { |
| case ISD::ADD: |
| case ISD::SUB: |
| case ISD::MUL: |
| case ISD::SHL: { |
| if (Op.getNode()->getFlags().hasNoSignedWrap()) |
| break; |
| [[fallthrough]]; |
| } |
| default: |
| NeedOF = true; |
| break; |
| } |
| break; |
| } |
| } |
| // See if we can use the CCR value from the operand instead of |
| // doing a separate TEST. TEST always sets OF and CF to 0, so unless |
| // we prove that the arithmetic won't overflow, we can't use OF or CF. |
| if (Op.getResNo() != 0 || NeedOF || NeedCF) { |
| // Emit a CMP with 0, which is the TEST pattern. |
| return DAG.getNode(M68kISD::CMP, DL, MVT::i8, |
| DAG.getConstant(0, DL, Op.getValueType()), Op); |
| } |
| unsigned Opcode = 0; |
| unsigned NumOperands = 0; |
| |
| // Truncate operations may prevent the merge of the SETCC instruction |
| // and the arithmetic instruction before it. Attempt to truncate the operands |
| // of the arithmetic instruction and use a reduced bit-width instruction. |
| bool NeedTruncation = false; |
| SDValue ArithOp = Op; |
| if (Op->getOpcode() == ISD::TRUNCATE && Op->hasOneUse()) { |
| SDValue Arith = Op->getOperand(0); |
| // Both the trunc and the arithmetic op need to have one user each. |
| if (Arith->hasOneUse()) |
| switch (Arith.getOpcode()) { |
| default: |
| break; |
| case ISD::ADD: |
| case ISD::SUB: |
| case ISD::AND: |
| case ISD::OR: |
| case ISD::XOR: { |
| NeedTruncation = true; |
| ArithOp = Arith; |
| } |
| } |
| } |
| |
| // NOTICE: In the code below we use ArithOp to hold the arithmetic operation |
| // which may be the result of a CAST. We use the variable 'Op', which is the |
| // non-casted variable when we check for possible users. |
| switch (ArithOp.getOpcode()) { |
| case ISD::ADD: |
| Opcode = M68kISD::ADD; |
| NumOperands = 2; |
| break; |
| case ISD::SHL: |
| case ISD::SRL: |
| // If we have a constant logical shift that's only used in a comparison |
| // against zero turn it into an equivalent AND. This allows turning it into |
| // a TEST instruction later. |
| if ((M68kCC == M68k::COND_EQ || M68kCC == M68k::COND_NE) && |
| Op->hasOneUse() && isa<ConstantSDNode>(Op->getOperand(1)) && |
| !hasNonFlagsUse(Op)) { |
| EVT VT = Op.getValueType(); |
| unsigned BitWidth = VT.getSizeInBits(); |
| unsigned ShAmt = Op->getConstantOperandVal(1); |
| if (ShAmt >= BitWidth) // Avoid undefined shifts. |
| break; |
| APInt Mask = ArithOp.getOpcode() == ISD::SRL |
| ? APInt::getHighBitsSet(BitWidth, BitWidth - ShAmt) |
| : APInt::getLowBitsSet(BitWidth, BitWidth - ShAmt); |
| if (!Mask.isSignedIntN(32)) // Avoid large immediates. |
| break; |
| Op = DAG.getNode(ISD::AND, DL, VT, Op->getOperand(0), |
| DAG.getConstant(Mask, DL, VT)); |
| } |
| break; |
| |
| case ISD::AND: |
| // If the primary 'and' result isn't used, don't bother using |
| // M68kISD::AND, because a TEST instruction will be better. |
| if (!hasNonFlagsUse(Op)) { |
| SDValue Op0 = ArithOp->getOperand(0); |
| SDValue Op1 = ArithOp->getOperand(1); |
| EVT VT = ArithOp.getValueType(); |
| bool IsAndn = isBitwiseNot(Op0) || isBitwiseNot(Op1); |
| bool IsLegalAndnType = VT == MVT::i32 || VT == MVT::i64; |
| |
| // But if we can combine this into an ANDN operation, then create an AND |
| // now and allow it to be pattern matched into an ANDN. |
| if (/*!Subtarget.hasBMI() ||*/ !IsAndn || !IsLegalAndnType) |
| break; |
| } |
| [[fallthrough]]; |
| case ISD::SUB: |
| case ISD::OR: |
| case ISD::XOR: |
| // Due to the ISEL shortcoming noted above, be conservative if this op is |
| // likely to be selected as part of a load-modify-store instruction. |
| for (const auto *U : Op.getNode()->uses()) |
| if (U->getOpcode() == ISD::STORE) |
| goto default_case; |
| |
| // Otherwise use a regular CCR-setting instruction. |
| switch (ArithOp.getOpcode()) { |
| default: |
| llvm_unreachable("unexpected operator!"); |
| case ISD::SUB: |
| Opcode = M68kISD::SUB; |
| break; |
| case ISD::XOR: |
| Opcode = M68kISD::XOR; |
| break; |
| case ISD::AND: |
| Opcode = M68kISD::AND; |
| break; |
| case ISD::OR: |
| Opcode = M68kISD::OR; |
| break; |
| } |
| |
| NumOperands = 2; |
| break; |
| case M68kISD::ADD: |
| case M68kISD::SUB: |
| case M68kISD::OR: |
| case M68kISD::XOR: |
| case M68kISD::AND: |
| return SDValue(Op.getNode(), 1); |
| default: |
| default_case: |
| break; |
| } |
| |
| // If we found that truncation is beneficial, perform the truncation and |
| // update 'Op'. |
| if (NeedTruncation) { |
| EVT VT = Op.getValueType(); |
| SDValue WideVal = Op->getOperand(0); |
| EVT WideVT = WideVal.getValueType(); |
| unsigned ConvertedOp = 0; |
| // Use a target machine opcode to prevent further DAGCombine |
| // optimizations that may separate the arithmetic operations |
| // from the setcc node. |
| switch (WideVal.getOpcode()) { |
| default: |
| break; |
| case ISD::ADD: |
| ConvertedOp = M68kISD::ADD; |
| break; |
| case ISD::SUB: |
| ConvertedOp = M68kISD::SUB; |
| break; |
| case ISD::AND: |
| ConvertedOp = M68kISD::AND; |
| break; |
| case ISD::OR: |
| ConvertedOp = M68kISD::OR; |
| break; |
| case ISD::XOR: |
| ConvertedOp = M68kISD::XOR; |
| break; |
| } |
| |
| if (ConvertedOp) { |
| const TargetLowering &TLI = DAG.getTargetLoweringInfo(); |
| if (TLI.isOperationLegal(WideVal.getOpcode(), WideVT)) { |
| SDValue V0 = DAG.getNode(ISD::TRUNCATE, DL, VT, WideVal.getOperand(0)); |
| SDValue V1 = DAG.getNode(ISD::TRUNCATE, DL, VT, WideVal.getOperand(1)); |
| Op = DAG.getNode(ConvertedOp, DL, VT, V0, V1); |
| } |
| } |
| } |
| |
| if (Opcode == 0) { |
| // Emit a CMP with 0, which is the TEST pattern. |
| return DAG.getNode(M68kISD::CMP, DL, MVT::i8, |
| DAG.getConstant(0, DL, Op.getValueType()), Op); |
| } |
| SDVTList VTs = DAG.getVTList(Op.getValueType(), MVT::i8); |
| SmallVector<SDValue, 4> Ops(Op->op_begin(), Op->op_begin() + NumOperands); |
| |
| SDValue New = DAG.getNode(Opcode, DL, VTs, Ops); |
| DAG.ReplaceAllUsesWith(Op, New); |
| return SDValue(New.getNode(), 1); |
| } |
| |
| /// \brief Return true if the condition is an unsigned comparison operation. |
| static bool isM68kCCUnsigned(unsigned M68kCC) { |
| switch (M68kCC) { |
| default: |
| llvm_unreachable("Invalid integer condition!"); |
| case M68k::COND_EQ: |
| case M68k::COND_NE: |
| case M68k::COND_CS: |
| case M68k::COND_HI: |
| case M68k::COND_LS: |
| case M68k::COND_CC: |
| return true; |
| case M68k::COND_GT: |
| case M68k::COND_GE: |
| case M68k::COND_LT: |
| case M68k::COND_LE: |
| return false; |
| } |
| } |
| |
| SDValue M68kTargetLowering::EmitCmp(SDValue Op0, SDValue Op1, unsigned M68kCC, |
| const SDLoc &DL, SelectionDAG &DAG) const { |
| if (isNullConstant(Op1)) |
| return EmitTest(Op0, M68kCC, DL, DAG); |
| |
| assert(!(isa<ConstantSDNode>(Op1) && Op0.getValueType() == MVT::i1) && |
| "Unexpected comparison operation for MVT::i1 operands"); |
| |
| if ((Op0.getValueType() == MVT::i8 || Op0.getValueType() == MVT::i16 || |
| Op0.getValueType() == MVT::i32 || Op0.getValueType() == MVT::i64)) { |
| // Only promote the compare up to I32 if it is a 16 bit operation |
| // with an immediate. 16 bit immediates are to be avoided. |
| if ((Op0.getValueType() == MVT::i16 && |
| (isa<ConstantSDNode>(Op0) || isa<ConstantSDNode>(Op1))) && |
| !DAG.getMachineFunction().getFunction().hasMinSize()) { |
| unsigned ExtendOp = |
| isM68kCCUnsigned(M68kCC) ? ISD::ZERO_EXTEND : ISD::SIGN_EXTEND; |
| Op0 = DAG.getNode(ExtendOp, DL, MVT::i32, Op0); |
| Op1 = DAG.getNode(ExtendOp, DL, MVT::i32, Op1); |
| } |
| // Use SUB instead of CMP to enable CSE between SUB and CMP. |
| SDVTList VTs = DAG.getVTList(Op0.getValueType(), MVT::i8); |
| SDValue Sub = DAG.getNode(M68kISD::SUB, DL, VTs, Op0, Op1); |
| return SDValue(Sub.getNode(), 1); |
| } |
| return DAG.getNode(M68kISD::CMP, DL, MVT::i8, Op0, Op1); |
| } |
| |
| /// Result of 'and' or 'trunc to i1' is compared against zero. |
| /// Change to a BTST node if possible. |
| SDValue M68kTargetLowering::LowerToBTST(SDValue Op, ISD::CondCode CC, |
| const SDLoc &DL, |
| SelectionDAG &DAG) const { |
| if (Op.getOpcode() == ISD::AND) |
| return LowerAndToBTST(Op, CC, DL, DAG); |
| if (Op.getOpcode() == ISD::TRUNCATE && Op.getValueType() == MVT::i1) |
| return LowerTruncateToBTST(Op, CC, DL, DAG); |
| return SDValue(); |
| } |
| |
| SDValue M68kTargetLowering::LowerSETCC(SDValue Op, SelectionDAG &DAG) const { |
| MVT VT = Op.getSimpleValueType(); |
| assert(VT == MVT::i8 && "SetCC type must be 8-bit integer"); |
| |
| SDValue Op0 = Op.getOperand(0); |
| SDValue Op1 = Op.getOperand(1); |
| SDLoc DL(Op); |
| ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(2))->get(); |
| |
| // Optimize to BTST if possible. |
| // Lower (X & (1 << N)) == 0 to BTST(X, N). |
| // Lower ((X >>u N) & 1) != 0 to BTST(X, N). |
| // Lower ((X >>s N) & 1) != 0 to BTST(X, N). |
| // Lower (trunc (X >> N) to i1) to BTST(X, N). |
| if (Op0.hasOneUse() && isNullConstant(Op1) && |
| (CC == ISD::SETEQ || CC == ISD::SETNE)) { |
| if (SDValue NewSetCC = LowerToBTST(Op0, CC, DL, DAG)) { |
| if (VT == MVT::i1) |
| return DAG.getNode(ISD::TRUNCATE, DL, MVT::i1, NewSetCC); |
| return NewSetCC; |
| } |
| } |
| |
| // Look for X == 0, X == 1, X != 0, or X != 1. We can simplify some forms of |
| // these. |
| if ((isOneConstant(Op1) || isNullConstant(Op1)) && |
| (CC == ISD::SETEQ || CC == ISD::SETNE)) { |
| |
| // If the input is a setcc, then reuse the input setcc or use a new one with |
| // the inverted condition. |
| if (Op0.getOpcode() == M68kISD::SETCC) { |
| M68k::CondCode CCode = (M68k::CondCode)Op0.getConstantOperandVal(0); |
| bool Invert = (CC == ISD::SETNE) ^ isNullConstant(Op1); |
| if (!Invert) |
| return Op0; |
| |
| CCode = M68k::GetOppositeBranchCondition(CCode); |
| SDValue SetCC = |
| DAG.getNode(M68kISD::SETCC, DL, MVT::i8, |
| DAG.getConstant(CCode, DL, MVT::i8), Op0.getOperand(1)); |
| if (VT == MVT::i1) |
| return DAG.getNode(ISD::TRUNCATE, DL, MVT::i1, SetCC); |
| return SetCC; |
| } |
| } |
| if (Op0.getValueType() == MVT::i1 && (CC == ISD::SETEQ || CC == ISD::SETNE)) { |
| if (isOneConstant(Op1)) { |
| ISD::CondCode NewCC = ISD::GlobalISel::getSetCCInverse(CC, true); |
| return DAG.getSetCC(DL, VT, Op0, DAG.getConstant(0, DL, MVT::i1), NewCC); |
| } |
| if (!isNullConstant(Op1)) { |
| SDValue Xor = DAG.getNode(ISD::XOR, DL, MVT::i1, Op0, Op1); |
| return DAG.getSetCC(DL, VT, Xor, DAG.getConstant(0, DL, MVT::i1), CC); |
| } |
| } |
| |
| bool IsFP = Op1.getSimpleValueType().isFloatingPoint(); |
| unsigned M68kCC = TranslateM68kCC(CC, DL, IsFP, Op0, Op1, DAG); |
| if (M68kCC == M68k::COND_INVALID) |
| return SDValue(); |
| |
| SDValue CCR = EmitCmp(Op0, Op1, M68kCC, DL, DAG); |
| return DAG.getNode(M68kISD::SETCC, DL, MVT::i8, |
| DAG.getConstant(M68kCC, DL, MVT::i8), CCR); |
| } |
| |
| SDValue M68kTargetLowering::LowerSETCCCARRY(SDValue Op, |
| SelectionDAG &DAG) const { |
| SDValue LHS = Op.getOperand(0); |
| SDValue RHS = Op.getOperand(1); |
| SDValue Carry = Op.getOperand(2); |
| SDValue Cond = Op.getOperand(3); |
| SDLoc DL(Op); |
| |
| assert(LHS.getSimpleValueType().isInteger() && "SETCCCARRY is integer only."); |
| M68k::CondCode CC = TranslateIntegerM68kCC(cast<CondCodeSDNode>(Cond)->get()); |
| |
| EVT CarryVT = Carry.getValueType(); |
| APInt NegOne = APInt::getAllOnes(CarryVT.getScalarSizeInBits()); |
| Carry = DAG.getNode(M68kISD::ADD, DL, DAG.getVTList(CarryVT, MVT::i32), Carry, |
| DAG.getConstant(NegOne, DL, CarryVT)); |
| |
| SDVTList VTs = DAG.getVTList(LHS.getValueType(), MVT::i32); |
| SDValue Cmp = |
| DAG.getNode(M68kISD::SUBX, DL, VTs, LHS, RHS, Carry.getValue(1)); |
| |
| return DAG.getNode(M68kISD::SETCC, DL, MVT::i8, |
| DAG.getConstant(CC, DL, MVT::i8), Cmp.getValue(1)); |
| } |
| |
| /// Return true if opcode is a M68k logical comparison. |
| static bool isM68kLogicalCmp(SDValue Op) { |
| unsigned Opc = Op.getNode()->getOpcode(); |
| if (Opc == M68kISD::CMP) |
| return true; |
| if (Op.getResNo() == 1 && |
| (Opc == M68kISD::ADD || Opc == M68kISD::SUB || Opc == M68kISD::ADDX || |
| Opc == M68kISD::SUBX || Opc == M68kISD::SMUL || Opc == M68kISD::UMUL || |
| Opc == M68kISD::OR || Opc == M68kISD::XOR || Opc == M68kISD::AND)) |
| return true; |
| |
| if (Op.getResNo() == 2 && Opc == M68kISD::UMUL) |
| return true; |
| |
| return false; |
| } |
| |
| static bool isTruncWithZeroHighBitsInput(SDValue V, SelectionDAG &DAG) { |
| if (V.getOpcode() != ISD::TRUNCATE) |
| return false; |
| |
| SDValue VOp0 = V.getOperand(0); |
| unsigned InBits = VOp0.getValueSizeInBits(); |
| unsigned Bits = V.getValueSizeInBits(); |
| return DAG.MaskedValueIsZero(VOp0, |
| APInt::getHighBitsSet(InBits, InBits - Bits)); |
| } |
| |
| SDValue M68kTargetLowering::LowerSELECT(SDValue Op, SelectionDAG &DAG) const { |
| bool addTest = true; |
| SDValue Cond = Op.getOperand(0); |
| SDValue Op1 = Op.getOperand(1); |
| SDValue Op2 = Op.getOperand(2); |
| SDLoc DL(Op); |
| SDValue CC; |
| |
| if (Cond.getOpcode() == ISD::SETCC) { |
| if (SDValue NewCond = LowerSETCC(Cond, DAG)) |
| Cond = NewCond; |
| } |
| |
| // (select (x == 0), -1, y) -> (sign_bit (x - 1)) | y |
| // (select (x == 0), y, -1) -> ~(sign_bit (x - 1)) | y |
| // (select (x != 0), y, -1) -> (sign_bit (x - 1)) | y |
| // (select (x != 0), -1, y) -> ~(sign_bit (x - 1)) | y |
| if (Cond.getOpcode() == M68kISD::SETCC && |
| Cond.getOperand(1).getOpcode() == M68kISD::CMP && |
| isNullConstant(Cond.getOperand(1).getOperand(0))) { |
| SDValue Cmp = Cond.getOperand(1); |
| |
| unsigned CondCode = Cond.getConstantOperandVal(0); |
| |
| if ((isAllOnesConstant(Op1) || isAllOnesConstant(Op2)) && |
| (CondCode == M68k::COND_EQ || CondCode == M68k::COND_NE)) { |
| SDValue Y = isAllOnesConstant(Op2) ? Op1 : Op2; |
| |
| SDValue CmpOp0 = Cmp.getOperand(1); |
| // Apply further optimizations for special cases |
| // (select (x != 0), -1, 0) -> neg & sbb |
| // (select (x == 0), 0, -1) -> neg & sbb |
| if (isNullConstant(Y) && |
| (isAllOnesConstant(Op1) == (CondCode == M68k::COND_NE))) { |
| |
| SDVTList VTs = DAG.getVTList(CmpOp0.getValueType(), MVT::i32); |
| |
| SDValue Neg = |
| DAG.getNode(M68kISD::SUB, DL, VTs, |
| DAG.getConstant(0, DL, CmpOp0.getValueType()), CmpOp0); |
| |
| SDValue Res = DAG.getNode(M68kISD::SETCC_CARRY, DL, Op.getValueType(), |
| DAG.getConstant(M68k::COND_CS, DL, MVT::i8), |
| SDValue(Neg.getNode(), 1)); |
| return Res; |
| } |
| |
| Cmp = DAG.getNode(M68kISD::CMP, DL, MVT::i8, |
| DAG.getConstant(1, DL, CmpOp0.getValueType()), CmpOp0); |
| |
| SDValue Res = // Res = 0 or -1. |
| DAG.getNode(M68kISD::SETCC_CARRY, DL, Op.getValueType(), |
| DAG.getConstant(M68k::COND_CS, DL, MVT::i8), Cmp); |
| |
| if (isAllOnesConstant(Op1) != (CondCode == M68k::COND_EQ)) |
| Res = DAG.getNOT(DL, Res, Res.getValueType()); |
| |
| if (!isNullConstant(Op2)) |
| Res = DAG.getNode(ISD::OR, DL, Res.getValueType(), Res, Y); |
| return Res; |
| } |
| } |
| |
| // Look past (and (setcc_carry (cmp ...)), 1). |
| if (Cond.getOpcode() == ISD::AND && |
| Cond.getOperand(0).getOpcode() == M68kISD::SETCC_CARRY && |
| isOneConstant(Cond.getOperand(1))) |
| Cond = Cond.getOperand(0); |
| |
| // If condition flag is set by a M68kISD::CMP, then use it as the condition |
| // setting operand in place of the M68kISD::SETCC. |
| unsigned CondOpcode = Cond.getOpcode(); |
| if (CondOpcode == M68kISD::SETCC || CondOpcode == M68kISD::SETCC_CARRY) { |
| CC = Cond.getOperand(0); |
| |
| SDValue Cmp = Cond.getOperand(1); |
| unsigned Opc = Cmp.getOpcode(); |
| |
| bool IllegalFPCMov = false; |
| |
| if ((isM68kLogicalCmp(Cmp) && !IllegalFPCMov) || Opc == M68kISD::BTST) { |
| Cond = Cmp; |
| addTest = false; |
| } |
| } else if (isOverflowArithmetic(CondOpcode)) { |
| // Result is unused here. |
| SDValue Result; |
| unsigned CCode; |
| lowerOverflowArithmetic(Cond, DAG, Result, Cond, CCode); |
| CC = DAG.getConstant(CCode, DL, MVT::i8); |
| addTest = false; |
| } |
| |
| if (addTest) { |
| // Look past the truncate if the high bits are known zero. |
| if (isTruncWithZeroHighBitsInput(Cond, DAG)) |
| Cond = Cond.getOperand(0); |
| |
| // We know the result of AND is compared against zero. Try to match |
| // it to BT. |
| if (Cond.getOpcode() == ISD::AND && Cond.hasOneUse()) { |
| if (SDValue NewSetCC = LowerToBTST(Cond, ISD::SETNE, DL, DAG)) { |
| CC = NewSetCC.getOperand(0); |
| Cond = NewSetCC.getOperand(1); |
| addTest = false; |
| } |
| } |
| } |
| |
| if (addTest) { |
| CC = DAG.getConstant(M68k::COND_NE, DL, MVT::i8); |
| Cond = EmitTest(Cond, M68k::COND_NE, DL, DAG); |
| } |
| |
| // a < b ? -1 : 0 -> RES = ~setcc_carry |
| // a < b ? 0 : -1 -> RES = setcc_carry |
| // a >= b ? -1 : 0 -> RES = setcc_carry |
| // a >= b ? 0 : -1 -> RES = ~setcc_carry |
| if (Cond.getOpcode() == M68kISD::SUB) { |
| unsigned CondCode = CC->getAsZExtVal(); |
| |
| if ((CondCode == M68k::COND_CC || CondCode == M68k::COND_CS) && |
| (isAllOnesConstant(Op1) || isAllOnesConstant(Op2)) && |
| (isNullConstant(Op1) || isNullConstant(Op2))) { |
| SDValue Res = |
| DAG.getNode(M68kISD::SETCC_CARRY, DL, Op.getValueType(), |
| DAG.getConstant(M68k::COND_CS, DL, MVT::i8), Cond); |
| if (isAllOnesConstant(Op1) != (CondCode == M68k::COND_CS)) |
| return DAG.getNOT(DL, Res, Res.getValueType()); |
| return Res; |
| } |
| } |
| |
| // M68k doesn't have an i8 cmov. If both operands are the result of a |
| // truncate widen the cmov and push the truncate through. This avoids |
| // introducing a new branch during isel and doesn't add any extensions. |
| if (Op.getValueType() == MVT::i8 && Op1.getOpcode() == ISD::TRUNCATE && |
| Op2.getOpcode() == ISD::TRUNCATE) { |
| SDValue T1 = Op1.getOperand(0), T2 = Op2.getOperand(0); |
| if (T1.getValueType() == T2.getValueType() && |
| // Block CopyFromReg so partial register stalls are avoided. |
| T1.getOpcode() != ISD::CopyFromReg && |
| T2.getOpcode() != ISD::CopyFromReg) { |
| SDVTList VTs = DAG.getVTList(T1.getValueType(), MVT::Glue); |
| SDValue Cmov = DAG.getNode(M68kISD::CMOV, DL, VTs, T2, T1, CC, Cond); |
| return DAG.getNode(ISD::TRUNCATE, DL, Op.getValueType(), Cmov); |
| } |
| } |
| |
| // Simple optimization when Cond is a constant to avoid generating |
| // M68kISD::CMOV if possible. |
| // TODO: Generalize this to use SelectionDAG::computeKnownBits. |
| if (auto *Const = dyn_cast<ConstantSDNode>(Cond.getNode())) { |
| const APInt &C = Const->getAPIntValue(); |
| if (C.countr_zero() >= 5) |
| return Op2; |
| else if (C.countr_one() >= 5) |
| return Op1; |
| } |
| |
| // M68kISD::CMOV means set the result (which is operand 1) to the RHS if |
| // condition is true. |
| SDVTList VTs = DAG.getVTList(Op.getValueType(), MVT::Glue); |
| SDValue Ops[] = {Op2, Op1, CC, Cond}; |
| return DAG.getNode(M68kISD::CMOV, DL, VTs, Ops); |
| } |
| |
| /// Return true if node is an ISD::AND or ISD::OR of two M68k::SETcc nodes |
| /// each of which has no other use apart from the AND / OR. |
| static bool isAndOrOfSetCCs(SDValue Op, unsigned &Opc) { |
| Opc = Op.getOpcode(); |
| if (Opc != ISD::OR && Opc != ISD::AND) |
| return false; |
| return (M68k::IsSETCC(Op.getOperand(0).getOpcode()) && |
| Op.getOperand(0).hasOneUse() && |
| M68k::IsSETCC(Op.getOperand(1).getOpcode()) && |
| Op.getOperand(1).hasOneUse()); |
| } |
| |
| /// Return true if node is an ISD::XOR of a M68kISD::SETCC and 1 and that the |
| /// SETCC node has a single use. |
| static bool isXor1OfSetCC(SDValue Op) { |
| if (Op.getOpcode() != ISD::XOR) |
| return false; |
| if (isOneConstant(Op.getOperand(1))) |
| return Op.getOperand(0).getOpcode() == M68kISD::SETCC && |
| Op.getOperand(0).hasOneUse(); |
| return false; |
| } |
| |
| SDValue M68kTargetLowering::LowerBRCOND(SDValue Op, SelectionDAG &DAG) const { |
| bool AddTest = true; |
| SDValue Chain = Op.getOperand(0); |
| SDValue Cond = Op.getOperand(1); |
| SDValue Dest = Op.getOperand(2); |
| SDLoc DL(Op); |
| SDValue CC; |
| bool Inverted = false; |
| |
| if (Cond.getOpcode() == ISD::SETCC) { |
| // Check for setcc([su]{add,sub}o == 0). |
| if (cast<CondCodeSDNode>(Cond.getOperand(2))->get() == ISD::SETEQ && |
| isNullConstant(Cond.getOperand(1)) && |
| Cond.getOperand(0).getResNo() == 1 && |
| (Cond.getOperand(0).getOpcode() == ISD::SADDO || |
| Cond.getOperand(0).getOpcode() == ISD::UADDO || |
| Cond.getOperand(0).getOpcode() == ISD::SSUBO || |
| Cond.getOperand(0).getOpcode() == ISD::USUBO)) { |
| Inverted = true; |
| Cond = Cond.getOperand(0); |
| } else { |
| if (SDValue NewCond = LowerSETCC(Cond, DAG)) |
| Cond = NewCond; |
| } |
| } |
| |
| // Look pass (and (setcc_carry (cmp ...)), 1). |
| if (Cond.getOpcode() == ISD::AND && |
| Cond.getOperand(0).getOpcode() == M68kISD::SETCC_CARRY && |
| isOneConstant(Cond.getOperand(1))) |
| Cond = Cond.getOperand(0); |
| |
| // If condition flag is set by a M68kISD::CMP, then use it as the condition |
| // setting operand in place of the M68kISD::SETCC. |
| unsigned CondOpcode = Cond.getOpcode(); |
| if (CondOpcode == M68kISD::SETCC || CondOpcode == M68kISD::SETCC_CARRY) { |
| CC = Cond.getOperand(0); |
| |
| SDValue Cmp = Cond.getOperand(1); |
| unsigned Opc = Cmp.getOpcode(); |
| |
| if (isM68kLogicalCmp(Cmp) || Opc == M68kISD::BTST) { |
| Cond = Cmp; |
| AddTest = false; |
| } else { |
| switch (CC->getAsZExtVal()) { |
| default: |
| break; |
| case M68k::COND_VS: |
| case M68k::COND_CS: |
| // These can only come from an arithmetic instruction with overflow, |
| // e.g. SADDO, UADDO. |
| Cond = Cond.getNode()->getOperand(1); |
| AddTest = false; |
| break; |
| } |
| } |
| } |
| CondOpcode = Cond.getOpcode(); |
| if (isOverflowArithmetic(CondOpcode)) { |
| SDValue Result; |
| unsigned CCode; |
| lowerOverflowArithmetic(Cond, DAG, Result, Cond, CCode); |
| |
| if (Inverted) |
| CCode = M68k::GetOppositeBranchCondition((M68k::CondCode)CCode); |
| CC = DAG.getConstant(CCode, DL, MVT::i8); |
| |
| AddTest = false; |
| } else { |
| unsigned CondOpc; |
| if (Cond.hasOneUse() && isAndOrOfSetCCs(Cond, CondOpc)) { |
| SDValue Cmp = Cond.getOperand(0).getOperand(1); |
| if (CondOpc == ISD::OR) { |
| // Also, recognize the pattern generated by an FCMP_UNE. We can emit |
| // two branches instead of an explicit OR instruction with a |
| // separate test. |
| if (Cmp == Cond.getOperand(1).getOperand(1) && isM68kLogicalCmp(Cmp)) { |
| CC = Cond.getOperand(0).getOperand(0); |
| Chain = DAG.getNode(M68kISD::BRCOND, DL, Op.getValueType(), Chain, |
| Dest, CC, Cmp); |
| CC = Cond.getOperand(1).getOperand(0); |
| Cond = Cmp; |
| AddTest = false; |
| } |
| } else { // ISD::AND |
| // Also, recognize the pattern generated by an FCMP_OEQ. We can emit |
| // two branches instead of an explicit AND instruction with a |
| // separate test. However, we only do this if this block doesn't |
| // have a fall-through edge, because this requires an explicit |
| // jmp when the condition is false. |
| if (Cmp == Cond.getOperand(1).getOperand(1) && isM68kLogicalCmp(Cmp) && |
| Op.getNode()->hasOneUse()) { |
| M68k::CondCode CCode = |
| (M68k::CondCode)Cond.getOperand(0).getConstantOperandVal(0); |
| CCode = M68k::GetOppositeBranchCondition(CCode); |
| CC = DAG.getConstant(CCode, DL, MVT::i8); |
| SDNode *User = *Op.getNode()->use_begin(); |
| // Look for an unconditional branch following this conditional branch. |
| // We need this because we need to reverse the successors in order |
| // to implement FCMP_OEQ. |
| if (User->getOpcode() == ISD::BR) { |
| SDValue FalseBB = User->getOperand(1); |
| SDNode *NewBR = |
| DAG.UpdateNodeOperands(User, User->getOperand(0), Dest); |
| assert(NewBR == User); |
| (void)NewBR; |
| Dest = FalseBB; |
| |
| Chain = DAG.getNode(M68kISD::BRCOND, DL, Op.getValueType(), Chain, |
| Dest, CC, Cmp); |
| M68k::CondCode CCode = |
| (M68k::CondCode)Cond.getOperand(1).getConstantOperandVal(0); |
| CCode = M68k::GetOppositeBranchCondition(CCode); |
| CC = DAG.getConstant(CCode, DL, MVT::i8); |
| Cond = Cmp; |
| AddTest = false; |
| } |
| } |
| } |
| } else if (Cond.hasOneUse() && isXor1OfSetCC(Cond)) { |
| // Recognize for xorb (setcc), 1 patterns. The xor inverts the condition. |
| // It should be transformed during dag combiner except when the condition |
| // is set by a arithmetics with overflow node. |
| M68k::CondCode CCode = |
| (M68k::CondCode)Cond.getOperand(0).getConstantOperandVal(0); |
| CCode = M68k::GetOppositeBranchCondition(CCode); |
| CC = DAG.getConstant(CCode, DL, MVT::i8); |
| Cond = Cond.getOperand(0).getOperand(1); |
| AddTest = false; |
| } |
| } |
| |
| if (AddTest) { |
| // Look pass the truncate if the high bits are known zero. |
| if (isTruncWithZeroHighBitsInput(Cond, DAG)) |
| Cond = Cond.getOperand(0); |
| |
| // We know the result is compared against zero. Try to match it to BT. |
| if (Cond.hasOneUse()) { |
| if (SDValue NewSetCC = LowerToBTST(Cond, ISD::SETNE, DL, DAG)) { |
| CC = NewSetCC.getOperand(0); |
| Cond = NewSetCC.getOperand(1); |
| AddTest = false; |
| } |
| } |
| } |
| |
| if (AddTest) { |
| M68k::CondCode MxCond = Inverted ? M68k::COND_EQ : M68k::COND_NE; |
| CC = DAG.getConstant(MxCond, DL, MVT::i8); |
| Cond = EmitTest(Cond, MxCond, DL, DAG); |
| } |
| return DAG.getNode(M68kISD::BRCOND, DL, Op.getValueType(), Chain, Dest, CC, |
| Cond); |
| } |
| |
| SDValue M68kTargetLowering::LowerADDC_ADDE_SUBC_SUBE(SDValue Op, |
| SelectionDAG &DAG) const { |
| MVT VT = Op.getNode()->getSimpleValueType(0); |
| |
| // Let legalize expand this if it isn't a legal type yet. |
| if (!DAG.getTargetLoweringInfo().isTypeLegal(VT)) |
| return SDValue(); |
| |
| SDVTList VTs = DAG.getVTList(VT, MVT::i8); |
| |
| unsigned Opc; |
| bool ExtraOp = false; |
| switch (Op.getOpcode()) { |
| default: |
| llvm_unreachable("Invalid code"); |
| case ISD::ADDC: |
| Opc = M68kISD::ADD; |
| break; |
| case ISD::ADDE: |
| Opc = M68kISD::ADDX; |
| ExtraOp = true; |
| break; |
| case ISD::SUBC: |
| Opc = M68kISD::SUB; |
| break; |
| case ISD::SUBE: |
| Opc = M68kISD::SUBX; |
| ExtraOp = true; |
| break; |
| } |
| |
| if (!ExtraOp) |
| return DAG.getNode(Opc, SDLoc(Op), VTs, Op.getOperand(0), Op.getOperand(1)); |
| return DAG.getNode(Opc, SDLoc(Op), VTs, Op.getOperand(0), Op.getOperand(1), |
| Op.getOperand(2)); |
| } |
| |
| // ConstantPool, JumpTable, GlobalAddress, and ExternalSymbol are lowered as |
| // their target countpart wrapped in the M68kISD::Wrapper node. Suppose N is |
| // one of the above mentioned nodes. It has to be wrapped because otherwise |
| // Select(N) returns N. So the raw TargetGlobalAddress nodes, etc. can only |
| // be used to form addressing mode. These wrapped nodes will be selected |
| // into MOV32ri. |
| SDValue M68kTargetLowering::LowerConstantPool(SDValue Op, |
| SelectionDAG &DAG) const { |
| ConstantPoolSDNode *CP = cast<ConstantPoolSDNode>(Op); |
| |
| // In PIC mode (unless we're in PCRel PIC mode) we add an offset to the |
| // global base reg. |
| unsigned char OpFlag = Subtarget.classifyLocalReference(nullptr); |
| |
| unsigned WrapperKind = M68kISD::Wrapper; |
| if (M68kII::isPCRelGlobalReference(OpFlag)) { |
| WrapperKind = M68kISD::WrapperPC; |
| } |
| |
| MVT PtrVT = getPointerTy(DAG.getDataLayout()); |
| SDValue Result = DAG.getTargetConstantPool( |
| CP->getConstVal(), PtrVT, CP->getAlign(), CP->getOffset(), OpFlag); |
| |
| SDLoc DL(CP); |
| Result = DAG.getNode(WrapperKind, DL, PtrVT, Result); |
| |
| // With PIC, the address is actually $g + Offset. |
| if (M68kII::isGlobalRelativeToPICBase(OpFlag)) { |
| Result = DAG.getNode(ISD::ADD, DL, PtrVT, |
| DAG.getNode(M68kISD::GLOBAL_BASE_REG, SDLoc(), PtrVT), |
| Result); |
| } |
| |
| return Result; |
| } |
| |
| SDValue M68kTargetLowering::LowerExternalSymbol(SDValue Op, |
| SelectionDAG &DAG) const { |
| const char *Sym = cast<ExternalSymbolSDNode>(Op)->getSymbol(); |
| |
| // In PIC mode (unless we're in PCRel PIC mode) we add an offset to the |
| // global base reg. |
| const Module *Mod = DAG.getMachineFunction().getFunction().getParent(); |
| unsigned char OpFlag = Subtarget.classifyExternalReference(*Mod); |
| |
| unsigned WrapperKind = M68kISD::Wrapper; |
| if (M68kII::isPCRelGlobalReference(OpFlag)) { |
| WrapperKind = M68kISD::WrapperPC; |
| } |
| |
| auto PtrVT = getPointerTy(DAG.getDataLayout()); |
| SDValue Result = DAG.getTargetExternalSymbol(Sym, PtrVT, OpFlag); |
| |
| SDLoc DL(Op); |
| Result = DAG.getNode(WrapperKind, DL, PtrVT, Result); |
| |
| // With PIC, the address is actually $g + Offset. |
| if (M68kII::isGlobalRelativeToPICBase(OpFlag)) { |
| Result = DAG.getNode(ISD::ADD, DL, PtrVT, |
| DAG.getNode(M68kISD::GLOBAL_BASE_REG, SDLoc(), PtrVT), |
| Result); |
| } |
| |
| // For symbols that require a load from a stub to get the address, emit the |
| // load. |
| if (M68kII::isGlobalStubReference(OpFlag)) { |
| Result = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(), Result, |
| MachinePointerInfo::getGOT(DAG.getMachineFunction())); |
| } |
| |
| return Result; |
| } |
| |
| SDValue M68kTargetLowering::LowerBlockAddress(SDValue Op, |
| SelectionDAG &DAG) const { |
| unsigned char OpFlags = Subtarget.classifyBlockAddressReference(); |
| const BlockAddress *BA = cast<BlockAddressSDNode>(Op)->getBlockAddress(); |
| int64_t Offset = cast<BlockAddressSDNode>(Op)->getOffset(); |
| SDLoc DL(Op); |
| auto PtrVT = getPointerTy(DAG.getDataLayout()); |
| |
| // Create the TargetBlockAddressAddress node. |
| SDValue Result = DAG.getTargetBlockAddress(BA, PtrVT, Offset, OpFlags); |
| |
| if (M68kII::isPCRelBlockReference(OpFlags)) { |
| Result = DAG.getNode(M68kISD::WrapperPC, DL, PtrVT, Result); |
| } else { |
| Result = DAG.getNode(M68kISD::Wrapper, DL, PtrVT, Result); |
| } |
| |
| // With PIC, the address is actually $g + Offset. |
| if (M68kII::isGlobalRelativeToPICBase(OpFlags)) { |
| Result = |
| DAG.getNode(ISD::ADD, DL, PtrVT, |
| DAG.getNode(M68kISD::GLOBAL_BASE_REG, DL, PtrVT), Result); |
| } |
| |
| return Result; |
| } |
| |
| SDValue M68kTargetLowering::LowerGlobalAddress(const GlobalValue *GV, |
| const SDLoc &DL, int64_t Offset, |
| SelectionDAG &DAG) const { |
| unsigned char OpFlags = Subtarget.classifyGlobalReference(GV); |
| auto PtrVT = getPointerTy(DAG.getDataLayout()); |
| |
| // Create the TargetGlobalAddress node, folding in the constant |
| // offset if it is legal. |
| SDValue Result; |
| if (M68kII::isDirectGlobalReference(OpFlags)) { |
| Result = DAG.getTargetGlobalAddress(GV, DL, PtrVT, Offset); |
| Offset = 0; |
| } else { |
| Result = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, OpFlags); |
| } |
| |
| if (M68kII::isPCRelGlobalReference(OpFlags)) |
| Result = DAG.getNode(M68kISD::WrapperPC, DL, PtrVT, Result); |
| else |
| Result = DAG.getNode(M68kISD::Wrapper, DL, PtrVT, Result); |
| |
| // With PIC, the address is actually $g + Offset. |
| if (M68kII::isGlobalRelativeToPICBase(OpFlags)) { |
| Result = |
| DAG.getNode(ISD::ADD, DL, PtrVT, |
| DAG.getNode(M68kISD::GLOBAL_BASE_REG, DL, PtrVT), Result); |
| } |
| |
| // For globals that require a load from a stub to get the address, emit the |
| // load. |
| if (M68kII::isGlobalStubReference(OpFlags)) { |
| Result = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(), Result, |
| MachinePointerInfo::getGOT(DAG.getMachineFunction())); |
| } |
| |
| // If there was a non-zero offset that we didn't fold, create an explicit |
| // addition for it. |
| if (Offset != 0) { |
| Result = DAG.getNode(ISD::ADD, DL, PtrVT, Result, |
| DAG.getConstant(Offset, DL, PtrVT)); |
| } |
| |
| return Result; |
| } |
| |
| SDValue M68kTargetLowering::LowerGlobalAddress(SDValue Op, |
| SelectionDAG &DAG) const { |
| const GlobalValue *GV = cast<GlobalAddressSDNode>(Op)->getGlobal(); |
| int64_t Offset = cast<GlobalAddressSDNode>(Op)->getOffset(); |
| return LowerGlobalAddress(GV, SDLoc(Op), Offset, DAG); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Custom Lower Jump Table |
| //===----------------------------------------------------------------------===// |
| |
| SDValue M68kTargetLowering::LowerJumpTable(SDValue Op, |
| SelectionDAG &DAG) const { |
| JumpTableSDNode *JT = cast<JumpTableSDNode>(Op); |
| |
| // In PIC mode (unless we're in PCRel PIC mode) we add an offset to the |
| // global base reg. |
| unsigned char OpFlag = Subtarget.classifyLocalReference(nullptr); |
| |
| unsigned WrapperKind = M68kISD::Wrapper; |
| if (M68kII::isPCRelGlobalReference(OpFlag)) { |
| WrapperKind = M68kISD::WrapperPC; |
| } |
| |
| auto PtrVT = getPointerTy(DAG.getDataLayout()); |
| SDValue Result = DAG.getTargetJumpTable(JT->getIndex(), PtrVT, OpFlag); |
| SDLoc DL(JT); |
| Result = DAG.getNode(WrapperKind, DL, PtrVT, Result); |
| |
| // With PIC, the address is actually $g + Offset. |
| if (M68kII::isGlobalRelativeToPICBase(OpFlag)) { |
| Result = DAG.getNode(ISD::ADD, DL, PtrVT, |
| DAG.getNode(M68kISD::GLOBAL_BASE_REG, SDLoc(), PtrVT), |
| Result); |
| } |
| |
| return Result; |
| } |
| |
| unsigned M68kTargetLowering::getJumpTableEncoding() const { |
| return Subtarget.getJumpTableEncoding(); |
| } |
| |
| const MCExpr *M68kTargetLowering::LowerCustomJumpTableEntry( |
| const MachineJumpTableInfo *MJTI, const MachineBasicBlock *MBB, |
| unsigned uid, MCContext &Ctx) const { |
| return MCSymbolRefExpr::create(MBB->getSymbol(), MCSymbolRefExpr::VK_GOTOFF, |
| Ctx); |
| } |
| |
| SDValue M68kTargetLowering::getPICJumpTableRelocBase(SDValue Table, |
| SelectionDAG &DAG) const { |
| if (getJumpTableEncoding() == MachineJumpTableInfo::EK_Custom32) |
| return DAG.getNode(M68kISD::GLOBAL_BASE_REG, SDLoc(), |
| getPointerTy(DAG.getDataLayout())); |
| |
| // MachineJumpTableInfo::EK_LabelDifference32 entry |
| return Table; |
| } |
| |
| // NOTE This only used for MachineJumpTableInfo::EK_LabelDifference32 entries |
| const MCExpr *M68kTargetLowering::getPICJumpTableRelocBaseExpr( |
| const MachineFunction *MF, unsigned JTI, MCContext &Ctx) const { |
| return MCSymbolRefExpr::create(MF->getJTISymbol(JTI, Ctx), Ctx); |
| } |
| |
| M68kTargetLowering::ConstraintType |
| M68kTargetLowering::getConstraintType(StringRef Constraint) const { |
| if (Constraint.size() > 0) { |
| switch (Constraint[0]) { |
| case 'a': |
| case 'd': |
| return C_RegisterClass; |
| case 'I': |
| case 'J': |
| case 'K': |
| case 'L': |
| case 'M': |
| case 'N': |
| case 'O': |
| case 'P': |
| return C_Immediate; |
| case 'C': |
| if (Constraint.size() == 2) |
| switch (Constraint[1]) { |
| case '0': |
| case 'i': |
| case 'j': |
| return C_Immediate; |
| default: |
| break; |
| } |
| break; |
| case 'Q': |
| case 'U': |
| return C_Memory; |
| default: |
| break; |
| } |
| } |
| |
| return TargetLowering::getConstraintType(Constraint); |
| } |
| |
| void M68kTargetLowering::LowerAsmOperandForConstraint(SDValue Op, |
| StringRef Constraint, |
| std::vector<SDValue> &Ops, |
| SelectionDAG &DAG) const { |
| SDValue Result; |
| |
| if (Constraint.size() == 1) { |
| // Constant constraints |
| switch (Constraint[0]) { |
| case 'I': |
| case 'J': |
| case 'K': |
| case 'L': |
| case 'M': |
| case 'N': |
| case 'O': |
| case 'P': { |
| auto *C = dyn_cast<ConstantSDNode>(Op); |
| if (!C) |
| return; |
| |
| int64_t Val = C->getSExtValue(); |
| switch (Constraint[0]) { |
| case 'I': // constant integer in the range [1,8] |
| if (Val > 0 && Val <= 8) |
| break; |
| return; |
| case 'J': // constant signed 16-bit integer |
| if (isInt<16>(Val)) |
| break; |
| return; |
| case 'K': // constant that is NOT in the range of [-0x80, 0x80) |
| if (Val < -0x80 || Val >= 0x80) |
| break; |
| return; |
| case 'L': // constant integer in the range [-8,-1] |
| if (Val < 0 && Val >= -8) |
| break; |
| return; |
| case 'M': // constant that is NOT in the range of [-0x100, 0x100] |
| if (Val < -0x100 || Val >= 0x100) |
| break; |
| return; |
| case 'N': // constant integer in the range [24,31] |
| if (Val >= 24 && Val <= 31) |
| break; |
| return; |
| case 'O': // constant integer 16 |
| if (Val == 16) |
| break; |
| return; |
| case 'P': // constant integer in the range [8,15] |
| if (Val >= 8 && Val <= 15) |
| break; |
| return; |
| default: |
| llvm_unreachable("Unhandled constant constraint"); |
| } |
| |
| Result = DAG.getTargetConstant(Val, SDLoc(Op), Op.getValueType()); |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| if (Constraint.size() == 2) { |
| switch (Constraint[0]) { |
| case 'C': |
| // Constant constraints start with 'C' |
| switch (Constraint[1]) { |
| case '0': |
| case 'i': |
| case 'j': { |
| auto *C = dyn_cast<ConstantSDNode>(Op); |
| if (!C) |
| break; |
| |
| int64_t Val = C->getSExtValue(); |
| switch (Constraint[1]) { |
| case '0': // constant integer 0 |
| if (!Val) |
| break; |
| return; |
| case 'i': // constant integer |
| break; |
| case 'j': // integer constant that doesn't fit in 16 bits |
| if (!isInt<16>(C->getSExtValue())) |
| break; |
| return; |
| default: |
| llvm_unreachable("Unhandled constant constraint"); |
| } |
| |
| Result = DAG.getTargetConstant(Val, SDLoc(Op), Op.getValueType()); |
| break; |
| } |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| if (Result.getNode()) { |
| Ops.push_back(Result); |
| return; |
| } |
| |
| TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, Ops, DAG); |
| } |
| |
| std::pair<unsigned, const TargetRegisterClass *> |
| M68kTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, |
| StringRef Constraint, |
| MVT VT) const { |
| if (Constraint.size() == 1) { |
| switch (Constraint[0]) { |
| case 'r': |
| case 'd': |
| switch (VT.SimpleTy) { |
| case MVT::i8: |
| return std::make_pair(0U, &M68k::DR8RegClass); |
| case MVT::i16: |
| return std::make_pair(0U, &M68k::DR16RegClass); |
| case MVT::i32: |
| return std::make_pair(0U, &M68k::DR32RegClass); |
| default: |
| break; |
| } |
| break; |
| case 'a': |
| switch (VT.SimpleTy) { |
| case MVT::i16: |
| return std::make_pair(0U, &M68k::AR16RegClass); |
| case MVT::i32: |
| return std::make_pair(0U, &M68k::AR32RegClass); |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); |
| } |
| |
| /// Determines whether the callee is required to pop its own arguments. |
| /// Callee pop is necessary to support tail calls. |
| bool M68k::isCalleePop(CallingConv::ID CC, bool IsVarArg, bool GuaranteeTCO) { |
| return CC == CallingConv::M68k_RTD && !IsVarArg; |
| } |
| |
| // Return true if it is OK for this CMOV pseudo-opcode to be cascaded |
| // together with other CMOV pseudo-opcodes into a single basic-block with |
| // conditional jump around it. |
| static bool isCMOVPseudo(MachineInstr &MI) { |
| switch (MI.getOpcode()) { |
| case M68k::CMOV8d: |
| case M68k::CMOV16d: |
| case M68k::CMOV32r: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| // The CCR operand of SelectItr might be missing a kill marker |
| // because there were multiple uses of CCR, and ISel didn't know |
| // which to mark. Figure out whether SelectItr should have had a |
| // kill marker, and set it if it should. Returns the correct kill |
| // marker value. |
| static bool checkAndUpdateCCRKill(MachineBasicBlock::iterator SelectItr, |
| MachineBasicBlock *BB, |
| const TargetRegisterInfo *TRI) { |
| // Scan forward through BB for a use/def of CCR. |
| MachineBasicBlock::iterator miI(std::next(SelectItr)); |
| for (MachineBasicBlock::iterator miE = BB->end(); miI != miE; ++miI) { |
| const MachineInstr &mi = *miI; |
| if (mi.readsRegister(M68k::CCR)) |
| return false; |
| if (mi.definesRegister(M68k::CCR)) |
| break; // Should have kill-flag - update below. |
| } |
| |
| // If we hit the end of the block, check whether CCR is live into a |
| // successor. |
| if (miI == BB->end()) |
| for (const auto *SBB : BB->successors()) |
| if (SBB->isLiveIn(M68k::CCR)) |
| return false; |
| |
| // We found a def, or hit the end of the basic block and CCR wasn't live |
| // out. SelectMI should have a kill flag on CCR. |
| SelectItr->addRegisterKilled(M68k::CCR, TRI); |
| return true; |
| } |
| |
| MachineBasicBlock * |
| M68kTargetLowering::EmitLoweredSelect(MachineInstr &MI, |
| MachineBasicBlock *MBB) const { |
| const TargetInstrInfo *TII = Subtarget.getInstrInfo(); |
| DebugLoc DL = MI.getDebugLoc(); |
| |
| // To "insert" a SELECT_CC instruction, we actually have to insert the |
| // diamond control-flow pattern. The incoming instruction knows the |
| // destination vreg to set, the condition code register to branch on, the |
| // true/false values to select between, and a branch opcode to use. |
| const BasicBlock *BB = MBB->getBasicBlock(); |
| MachineFunction::iterator It = ++MBB->getIterator(); |
| |
| // ThisMBB: |
| // ... |
| // TrueVal = ... |
| // cmp ccX, r1, r2 |
| // bcc Copy1MBB |
| // fallthrough --> Copy0MBB |
| MachineBasicBlock *ThisMBB = MBB; |
| MachineFunction *F = MBB->getParent(); |
| |
| // This code lowers all pseudo-CMOV instructions. Generally it lowers these |
| // as described above, by inserting a MBB, and then making a PHI at the join |
| // point to select the true and false operands of the CMOV in the PHI. |
| // |
| // The code also handles two different cases of multiple CMOV opcodes |
| // in a row. |
| // |
| // Case 1: |
| // In this case, there are multiple CMOVs in a row, all which are based on |
| // the same condition setting (or the exact opposite condition setting). |
| // In this case we can lower all the CMOVs using a single inserted MBB, and |
| // then make a number of PHIs at the join point to model the CMOVs. The only |
| // trickiness here, is that in a case like: |
| // |
| // t2 = CMOV cond1 t1, f1 |
| // t3 = CMOV cond1 t2, f2 |
| // |
| // when rewriting this into PHIs, we have to perform some renaming on the |
| // temps since you cannot have a PHI operand refer to a PHI result earlier |
| // in the same block. The "simple" but wrong lowering would be: |
| // |
| // t2 = PHI t1(BB1), f1(BB2) |
| // t3 = PHI t2(BB1), f2(BB2) |
| // |
| // but clearly t2 is not defined in BB1, so that is incorrect. The proper |
| // renaming is to note that on the path through BB1, t2 is really just a |
| // copy of t1, and do that renaming, properly generating: |
| // |
| // t2 = PHI t1(BB1), f1(BB2) |
| // t3 = PHI t1(BB1), f2(BB2) |
| // |
| // Case 2, we lower cascaded CMOVs such as |
| // |
| // (CMOV (CMOV F, T, cc1), T, cc2) |
| // |
| // to two successives branches. |
| MachineInstr *CascadedCMOV = nullptr; |
| MachineInstr *LastCMOV = &MI; |
| M68k::CondCode CC = M68k::CondCode(MI.getOperand(3).getImm()); |
| M68k::CondCode OppCC = M68k::GetOppositeBranchCondition(CC); |
| MachineBasicBlock::iterator NextMIIt = |
| std::next(MachineBasicBlock::iterator(MI)); |
| |
| // Check for case 1, where there are multiple CMOVs with the same condition |
| // first. Of the two cases of multiple CMOV lowerings, case 1 reduces the |
| // number of jumps the most. |
| |
| if (isCMOVPseudo(MI)) { |
| // See if we have a string of CMOVS with the same condition. |
| while (NextMIIt != MBB->end() && isCMOVPseudo(*NextMIIt) && |
| (NextMIIt->getOperand(3).getImm() == CC || |
| NextMIIt->getOperand(3).getImm() == OppCC)) { |
| LastCMOV = &*NextMIIt; |
| ++NextMIIt; |
| } |
| } |
| |
| // This checks for case 2, but only do this if we didn't already find |
| // case 1, as indicated by LastCMOV == MI. |
| if (LastCMOV == &MI && NextMIIt != MBB->end() && |
| NextMIIt->getOpcode() == MI.getOpcode() && |
| NextMIIt->getOperand(2).getReg() == MI.getOperand(2).getReg() && |
| NextMIIt->getOperand(1).getReg() == MI.getOperand(0).getReg() && |
| NextMIIt->getOperand(1).isKill()) { |
| CascadedCMOV = &*NextMIIt; |
| } |
| |
| MachineBasicBlock *Jcc1MBB = nullptr; |
| |
| // If we have a cascaded CMOV, we lower it to two successive branches to |
| // the same block. CCR is used by both, so mark it as live in the second. |
| if (CascadedCMOV) { |
| Jcc1MBB = F->CreateMachineBasicBlock(BB); |
| F->insert(It, Jcc1MBB); |
| Jcc1MBB->addLiveIn(M68k::CCR); |
| } |
| |
| MachineBasicBlock *Copy0MBB = F->CreateMachineBasicBlock(BB); |
| MachineBasicBlock *SinkMBB = F->CreateMachineBasicBlock(BB); |
| F->insert(It, Copy0MBB); |
| F->insert(It, SinkMBB); |
| |
| // Set the call frame size on entry to the new basic blocks. |
| unsigned CallFrameSize = TII->getCallFrameSizeAt(MI); |
| Copy0MBB->setCallFrameSize(CallFrameSize); |
| SinkMBB->setCallFrameSize(CallFrameSize); |
| |
| // If the CCR register isn't dead in the terminator, then claim that it's |
| // live into the sink and copy blocks. |
| const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo(); |
| |
| MachineInstr *LastCCRSUser = CascadedCMOV ? CascadedCMOV : LastCMOV; |
| if (!LastCCRSUser->killsRegister(M68k::CCR) && |
| !checkAndUpdateCCRKill(LastCCRSUser, MBB, TRI)) { |
| Copy0MBB->addLiveIn(M68k::CCR); |
| SinkMBB->addLiveIn(M68k::CCR); |
| } |
| |
| // Transfer the remainder of MBB and its successor edges to SinkMBB. |
| SinkMBB->splice(SinkMBB->begin(), MBB, |
| std::next(MachineBasicBlock::iterator(LastCMOV)), MBB->end()); |
| SinkMBB->transferSuccessorsAndUpdatePHIs(MBB); |
| |
| // Add the true and fallthrough blocks as its successors. |
| if (CascadedCMOV) { |
| // The fallthrough block may be Jcc1MBB, if we have a cascaded CMOV. |
| MBB->addSuccessor(Jcc1MBB); |
| |
| // In that case, Jcc1MBB will itself fallthrough the Copy0MBB, and |
| // jump to the SinkMBB. |
| Jcc1MBB->addSuccessor(Copy0MBB); |
| Jcc1MBB->addSuccessor(SinkMBB); |
| } else { |
| MBB->addSuccessor(Copy0MBB); |
| } |
| |
| // The true block target of the first (or only) branch is always SinkMBB. |
| MBB->addSuccessor(SinkMBB); |
| |
| // Create the conditional branch instruction. |
| unsigned Opc = M68k::GetCondBranchFromCond(CC); |
| BuildMI(MBB, DL, TII->get(Opc)).addMBB(SinkMBB); |
| |
| if (CascadedCMOV) { |
| unsigned Opc2 = M68k::GetCondBranchFromCond( |
| (M68k::CondCode)CascadedCMOV->getOperand(3).getImm()); |
| BuildMI(Jcc1MBB, DL, TII->get(Opc2)).addMBB(SinkMBB); |
| } |
| |
| // Copy0MBB: |
| // %FalseValue = ... |
| // # fallthrough to SinkMBB |
| Copy0MBB->addSuccessor(SinkMBB); |
| |
| // SinkMBB: |
| // %Result = phi [ %FalseValue, Copy0MBB ], [ %TrueValue, ThisMBB ] |
| // ... |
| MachineBasicBlock::iterator MIItBegin = MachineBasicBlock::iterator(MI); |
| MachineBasicBlock::iterator MIItEnd = |
| std::next(MachineBasicBlock::iterator(LastCMOV)); |
| MachineBasicBlock::iterator SinkInsertionPoint = SinkMBB->begin(); |
| DenseMap<unsigned, std::pair<unsigned, unsigned>> RegRewriteTable; |
| MachineInstrBuilder MIB; |
| |
| // As we are creating the PHIs, we have to be careful if there is more than |
| // one. Later CMOVs may reference the results of earlier CMOVs, but later |
| // PHIs have to reference the individual true/false inputs from earlier PHIs. |
| // That also means that PHI construction must work forward from earlier to |
| // later, and that the code must maintain a mapping from earlier PHI's |
| // destination registers, and the registers that went into the PHI. |
| |
| for (MachineBasicBlock::iterator MIIt = MIItBegin; MIIt != MIItEnd; ++MIIt) { |
| Register DestReg = MIIt->getOperand(0).getReg(); |
| Register Op1Reg = MIIt->getOperand(1).getReg(); |
| Register Op2Reg = MIIt->getOperand(2).getReg(); |
| |
| // If this CMOV we are generating is the opposite condition from |
| // the jump we generated, then we have to swap the operands for the |
| // PHI that is going to be generated. |
| if (MIIt->getOperand(3).getImm() == OppCC) |
| std::swap(Op1Reg, Op2Reg); |
| |
| if (RegRewriteTable.find(Op1Reg) != RegRewriteTable.end()) |
| Op1Reg = RegRewriteTable[Op1Reg].first; |
| |
| if (RegRewriteTable.find(Op2Reg) != RegRewriteTable.end()) |
| Op2Reg = RegRewriteTable[Op2Reg].second; |
| |
| MIB = |
| BuildMI(*SinkMBB, SinkInsertionPoint, DL, TII->get(M68k::PHI), DestReg) |
| .addReg(Op1Reg) |
| .addMBB(Copy0MBB) |
| .addReg(Op2Reg) |
| .addMBB(ThisMBB); |
| |
| // Add this PHI to the rewrite table. |
| RegRewriteTable[DestReg] = std::make_pair(Op1Reg, Op2Reg); |
| } |
| |
| // If we have a cascaded CMOV, the second Jcc provides the same incoming |
| // value as the first Jcc (the True operand of the SELECT_CC/CMOV nodes). |
| if (CascadedCMOV) { |
| MIB.addReg(MI.getOperand(2).getReg()).addMBB(Jcc1MBB); |
| // Copy the PHI result to the register defined by the second CMOV. |
| BuildMI(*SinkMBB, std::next(MachineBasicBlock::iterator(MIB.getInstr())), |
| DL, TII->get(TargetOpcode::COPY), |
| CascadedCMOV->getOperand(0).getReg()) |
| .addReg(MI.getOperand(0).getReg()); |
| CascadedCMOV->eraseFromParent(); |
| } |
| |
| // Now remove the CMOV(s). |
| for (MachineBasicBlock::iterator MIIt = MIItBegin; MIIt != MIItEnd;) |
| (MIIt++)->eraseFromParent(); |
| |
| return SinkMBB; |
| } |
| |
| MachineBasicBlock * |
| M68kTargetLowering::EmitLoweredSegAlloca(MachineInstr &MI, |
| MachineBasicBlock *BB) const { |
| llvm_unreachable("Cannot lower Segmented Stack Alloca with stack-split on"); |
| } |
| |
| MachineBasicBlock * |
| M68kTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, |
| MachineBasicBlock *BB) const { |
| switch (MI.getOpcode()) { |
| default: |
| llvm_unreachable("Unexpected instr type to insert"); |
| case M68k::CMOV8d: |
| case M68k::CMOV16d: |
| case M68k::CMOV32r: |
| return EmitLoweredSelect(MI, BB); |
| case M68k::SALLOCA: |
| return EmitLoweredSegAlloca(MI, BB); |
| } |
| } |
| |
| SDValue M68kTargetLowering::LowerVASTART(SDValue Op, SelectionDAG &DAG) const { |
| MachineFunction &MF = DAG.getMachineFunction(); |
| auto PtrVT = getPointerTy(MF.getDataLayout()); |
| M68kMachineFunctionInfo *FuncInfo = MF.getInfo<M68kMachineFunctionInfo>(); |
| |
| const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue(); |
| SDLoc DL(Op); |
| |
| // vastart just stores the address of the VarArgsFrameIndex slot into the |
| // memory location argument. |
| SDValue FR = DAG.getFrameIndex(FuncInfo->getVarArgsFrameIndex(), PtrVT); |
| return DAG.getStore(Op.getOperand(0), DL, FR, Op.getOperand(1), |
| MachinePointerInfo(SV)); |
| } |
| |
| SDValue M68kTargetLowering::LowerATOMICFENCE(SDValue Op, |
| SelectionDAG &DAG) const { |
| // Lower to a memory barrier created from inline asm. |
| const TargetLowering &TLI = DAG.getTargetLoweringInfo(); |
| LLVMContext &Ctx = *DAG.getContext(); |
| |
| const unsigned Flags = InlineAsm::Extra_MayLoad | InlineAsm::Extra_MayStore | |
| InlineAsm::Extra_HasSideEffects; |
| const SDValue AsmOperands[4] = { |
| Op.getOperand(0), // Input chain |
| DAG.getTargetExternalSymbol( |
| "", TLI.getProgramPointerTy( |
| DAG.getDataLayout())), // Empty inline asm string |
| DAG.getMDNode(MDNode::get(Ctx, {})), // (empty) srcloc |
| DAG.getTargetConstant(Flags, SDLoc(Op), |
| TLI.getPointerTy(DAG.getDataLayout())), // Flags |
| }; |
| |
| return DAG.getNode(ISD::INLINEASM, SDLoc(Op), |
| DAG.getVTList(MVT::Other, MVT::Glue), AsmOperands); |
| } |
| |
| // Lower dynamic stack allocation to _alloca call for Cygwin/Mingw targets. |
| // Calls to _alloca are needed to probe the stack when allocating more than 4k |
| // bytes in one go. Touching the stack at 4K increments is necessary to ensure |
| // that the guard pages used by the OS virtual memory manager are allocated in |
| // correct sequence. |
| SDValue M68kTargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op, |
| SelectionDAG &DAG) const { |
| MachineFunction &MF = DAG.getMachineFunction(); |
| bool SplitStack = MF.shouldSplitStack(); |
| |
| SDLoc DL(Op); |
| |
| // Get the inputs. |
| SDNode *Node = Op.getNode(); |
| SDValue Chain = Op.getOperand(0); |
| SDValue Size = Op.getOperand(1); |
| unsigned Align = Op.getConstantOperandVal(2); |
| EVT VT = Node->getValueType(0); |
| |
| // Chain the dynamic stack allocation so that it doesn't modify the stack |
| // pointer when other instructions are using the stack. |
| Chain = DAG.getCALLSEQ_START(Chain, 0, 0, DL); |
| |
| SDValue Result; |
| if (SplitStack) { |
| auto &MRI = MF.getRegInfo(); |
| auto SPTy = getPointerTy(DAG.getDataLayout()); |
| auto *ARClass = getRegClassFor(SPTy); |
| Register Vreg = MRI.createVirtualRegister(ARClass); |
| Chain = DAG.getCopyToReg(Chain, DL, Vreg, Size); |
| Result = DAG.getNode(M68kISD::SEG_ALLOCA, DL, SPTy, Chain, |
| DAG.getRegister(Vreg, SPTy)); |
| } else { |
| auto &TLI = DAG.getTargetLoweringInfo(); |
| Register SPReg = TLI.getStackPointerRegisterToSaveRestore(); |
| assert(SPReg && "Target cannot require DYNAMIC_STACKALLOC expansion and" |
| " not tell us which reg is the stack pointer!"); |
| |
| SDValue SP = DAG.getCopyFromReg(Chain, DL, SPReg, VT); |
| Chain = SP.getValue(1); |
| const TargetFrameLowering &TFI = *Subtarget.getFrameLowering(); |
| unsigned StackAlign = TFI.getStackAlignment(); |
| Result = DAG.getNode(ISD::SUB, DL, VT, SP, Size); // Value |
| if (Align > StackAlign) |
| Result = DAG.getNode(ISD::AND, DL, VT, Result, |
| DAG.getConstant(-(uint64_t)Align, DL, VT)); |
| Chain = DAG.getCopyToReg(Chain, DL, SPReg, Result); // Output chain |
| } |
| |
| Chain = DAG.getCALLSEQ_END(Chain, 0, 0, SDValue(), DL); |
| |
| SDValue Ops[2] = {Result, Chain}; |
| return DAG.getMergeValues(Ops, DL); |
| } |
| |
| SDValue M68kTargetLowering::LowerShiftLeftParts(SDValue Op, |
| SelectionDAG &DAG) const { |
| SDLoc DL(Op); |
| SDValue Lo = Op.getOperand(0); |
| SDValue Hi = Op.getOperand(1); |
| SDValue Shamt = Op.getOperand(2); |
| EVT VT = Lo.getValueType(); |
| |
| // if Shamt - register size < 0: // Shamt < register size |
| // Lo = Lo << Shamt |
| // Hi = (Hi << Shamt) | ((Lo >>u 1) >>u (register size - 1 ^ Shamt)) |
| // else: |
| // Lo = 0 |
| // Hi = Lo << (Shamt - register size) |
| |
| SDValue Zero = DAG.getConstant(0, DL, VT); |
| SDValue One = DAG.getConstant(1, DL, VT); |
| 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 RegisterSizeMinus1Shamt = |
| DAG.getNode(ISD::XOR, DL, VT, RegisterSizeMinus1, Shamt); |
| |
| SDValue LoTrue = DAG.getNode(ISD::SHL, DL, VT, Lo, Shamt); |
| SDValue ShiftRight1Lo = DAG.getNode(ISD::SRL, DL, VT, Lo, One); |
| SDValue ShiftRightLo = |
| DAG.getNode(ISD::SRL, DL, VT, ShiftRight1Lo, RegisterSizeMinus1Shamt); |
| SDValue ShiftLeftHi = DAG.getNode(ISD::SHL, DL, VT, Hi, Shamt); |
| SDValue HiTrue = DAG.getNode(ISD::OR, DL, VT, ShiftLeftHi, ShiftRightLo); |
| SDValue HiFalse = DAG.getNode(ISD::SHL, DL, VT, Lo, ShamtMinusRegisterSize); |
| |
| SDValue CC = |
| DAG.getSetCC(DL, MVT::i8, ShamtMinusRegisterSize, Zero, ISD::SETLT); |
| |
| Lo = DAG.getNode(ISD::SELECT, DL, VT, CC, LoTrue, Zero); |
| Hi = DAG.getNode(ISD::SELECT, DL, VT, CC, HiTrue, HiFalse); |
| |
| return DAG.getMergeValues({Lo, Hi}, DL); |
| } |
| |
| SDValue M68kTargetLowering::LowerShiftRightParts(SDValue Op, SelectionDAG &DAG, |
| bool IsSRA) const { |
| SDLoc DL(Op); |
| SDValue Lo = Op.getOperand(0); |
| SDValue Hi = Op.getOperand(1); |
| SDValue Shamt = Op.getOperand(2); |
| EVT VT = Lo.getValueType(); |
| |
| // SRA expansion: |
| // if Shamt - register size < 0: // Shamt < register size |
| // Lo = (Lo >>u Shamt) | ((Hi << 1) << (register size - 1 ^ 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 << 1) << (register size - 1 ^ Shamt)) |
| // Hi = Hi >>u Shamt |
| // else: |
| // Lo = Hi >>u (Shamt - register size); |
| // Hi = 0; |
| |
| unsigned ShiftRightOp = IsSRA ? ISD::SRA : ISD::SRL; |
| |
| SDValue Zero = DAG.getConstant(0, DL, VT); |
| SDValue One = DAG.getConstant(1, DL, VT); |
| 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 RegisterSizeMinus1Shamt = |
| DAG.getNode(ISD::XOR, DL, VT, RegisterSizeMinus1, Shamt); |
| |
| SDValue ShiftRightLo = DAG.getNode(ISD::SRL, DL, VT, Lo, Shamt); |
| SDValue ShiftLeftHi1 = DAG.getNode(ISD::SHL, DL, VT, Hi, One); |
| SDValue ShiftLeftHi = |
| DAG.getNode(ISD::SHL, DL, VT, ShiftLeftHi1, RegisterSizeMinus1Shamt); |
| SDValue LoTrue = DAG.getNode(ISD::OR, DL, VT, ShiftRightLo, ShiftLeftHi); |
| SDValue HiTrue = DAG.getNode(ShiftRightOp, DL, VT, Hi, Shamt); |
| SDValue LoFalse = |
| DAG.getNode(ShiftRightOp, DL, VT, Hi, ShamtMinusRegisterSize); |
| SDValue HiFalse = |
| IsSRA ? DAG.getNode(ISD::SRA, DL, VT, Hi, RegisterSizeMinus1) : Zero; |
| |
| SDValue CC = |
| DAG.getSetCC(DL, MVT::i8, ShamtMinusRegisterSize, Zero, ISD::SETLT); |
| |
| Lo = DAG.getNode(ISD::SELECT, DL, VT, CC, LoTrue, LoFalse); |
| Hi = DAG.getNode(ISD::SELECT, DL, VT, CC, HiTrue, HiFalse); |
| |
| return DAG.getMergeValues({Lo, Hi}, DL); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // DAG Combine |
| //===----------------------------------------------------------------------===// |
| |
| static SDValue getSETCC(M68k::CondCode Cond, SDValue CCR, const SDLoc &dl, |
| SelectionDAG &DAG) { |
| return DAG.getNode(M68kISD::SETCC, dl, MVT::i8, |
| DAG.getConstant(Cond, dl, MVT::i8), CCR); |
| } |
| // When legalizing carry, we create carries via add X, -1 |
| // If that comes from an actual carry, via setcc, we use the |
| // carry directly. |
| static SDValue combineCarryThroughADD(SDValue CCR) { |
| if (CCR.getOpcode() == M68kISD::ADD) { |
| if (isAllOnesConstant(CCR.getOperand(1))) { |
| SDValue Carry = CCR.getOperand(0); |
| while (Carry.getOpcode() == ISD::TRUNCATE || |
| Carry.getOpcode() == ISD::ZERO_EXTEND || |
| Carry.getOpcode() == ISD::SIGN_EXTEND || |
| Carry.getOpcode() == ISD::ANY_EXTEND || |
| (Carry.getOpcode() == ISD::AND && |
| isOneConstant(Carry.getOperand(1)))) |
| Carry = Carry.getOperand(0); |
| if (Carry.getOpcode() == M68kISD::SETCC || |
| Carry.getOpcode() == M68kISD::SETCC_CARRY) { |
| if (Carry.getConstantOperandVal(0) == M68k::COND_CS) |
| return Carry.getOperand(1); |
| } |
| } |
| } |
| |
| return SDValue(); |
| } |
| |
| /// Optimize a CCR definition used according to the condition code \p CC into |
| /// a simpler CCR value, potentially returning a new \p CC and replacing uses |
| /// of chain values. |
| static SDValue combineSetCCCCR(SDValue CCR, M68k::CondCode &CC, |
| SelectionDAG &DAG, |
| const M68kSubtarget &Subtarget) { |
| if (CC == M68k::COND_CS) |
| if (SDValue Flags = combineCarryThroughADD(CCR)) |
| return Flags; |
| |
| return SDValue(); |
| } |
| |
| // Optimize RES = M68kISD::SETCC CONDCODE, CCR_INPUT |
| static SDValue combineM68kSetCC(SDNode *N, SelectionDAG &DAG, |
| const M68kSubtarget &Subtarget) { |
| SDLoc DL(N); |
| M68k::CondCode CC = M68k::CondCode(N->getConstantOperandVal(0)); |
| SDValue CCR = N->getOperand(1); |
| |
| // Try to simplify the CCR and condition code operands. |
| if (SDValue Flags = combineSetCCCCR(CCR, CC, DAG, Subtarget)) |
| return getSETCC(CC, Flags, DL, DAG); |
| |
| return SDValue(); |
| } |
| static SDValue combineM68kBrCond(SDNode *N, SelectionDAG &DAG, |
| const M68kSubtarget &Subtarget) { |
| SDLoc DL(N); |
| M68k::CondCode CC = M68k::CondCode(N->getConstantOperandVal(2)); |
| SDValue CCR = N->getOperand(3); |
| |
| // Try to simplify the CCR and condition code operands. |
| // Make sure to not keep references to operands, as combineSetCCCCR can |
| // RAUW them under us. |
| if (SDValue Flags = combineSetCCCCR(CCR, CC, DAG, Subtarget)) { |
| SDValue Cond = DAG.getConstant(CC, DL, MVT::i8); |
| return DAG.getNode(M68kISD::BRCOND, DL, N->getVTList(), N->getOperand(0), |
| N->getOperand(1), Cond, Flags); |
| } |
| |
| return SDValue(); |
| } |
| |
| static SDValue combineSUBX(SDNode *N, SelectionDAG &DAG) { |
| if (SDValue Flags = combineCarryThroughADD(N->getOperand(2))) { |
| MVT VT = N->getSimpleValueType(0); |
| SDVTList VTs = DAG.getVTList(VT, MVT::i32); |
| return DAG.getNode(M68kISD::SUBX, SDLoc(N), VTs, N->getOperand(0), |
| N->getOperand(1), Flags); |
| } |
| |
| return SDValue(); |
| } |
| |
| // Optimize RES, CCR = M68kISD::ADDX LHS, RHS, CCR |
| static SDValue combineADDX(SDNode *N, SelectionDAG &DAG, |
| TargetLowering::DAGCombinerInfo &DCI) { |
| if (SDValue Flags = combineCarryThroughADD(N->getOperand(2))) { |
| MVT VT = N->getSimpleValueType(0); |
| SDVTList VTs = DAG.getVTList(VT, MVT::i32); |
| return DAG.getNode(M68kISD::ADDX, SDLoc(N), VTs, N->getOperand(0), |
| N->getOperand(1), Flags); |
| } |
| |
| return SDValue(); |
| } |
| |
| SDValue M68kTargetLowering::PerformDAGCombine(SDNode *N, |
| DAGCombinerInfo &DCI) const { |
| SelectionDAG &DAG = DCI.DAG; |
| switch (N->getOpcode()) { |
| case M68kISD::SUBX: |
| return combineSUBX(N, DAG); |
| case M68kISD::ADDX: |
| return combineADDX(N, DAG, DCI); |
| case M68kISD::SETCC: |
| return combineM68kSetCC(N, DAG, Subtarget); |
| case M68kISD::BRCOND: |
| return combineM68kBrCond(N, DAG, Subtarget); |
| } |
| |
| return SDValue(); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // M68kISD Node Names |
| //===----------------------------------------------------------------------===// |
| const char *M68kTargetLowering::getTargetNodeName(unsigned Opcode) const { |
| switch (Opcode) { |
| case M68kISD::CALL: |
| return "M68kISD::CALL"; |
| case M68kISD::TAIL_CALL: |
| return "M68kISD::TAIL_CALL"; |
| case M68kISD::RET: |
| return "M68kISD::RET"; |
| case M68kISD::TC_RETURN: |
| return "M68kISD::TC_RETURN"; |
| case M68kISD::ADD: |
| return "M68kISD::ADD"; |
| case M68kISD::SUB: |
| return "M68kISD::SUB"; |
| case M68kISD::ADDX: |
| return "M68kISD::ADDX"; |
| case M68kISD::SUBX: |
| return "M68kISD::SUBX"; |
| case M68kISD::SMUL: |
| return "M68kISD::SMUL"; |
| case M68kISD::UMUL: |
| return "M68kISD::UMUL"; |
| case M68kISD::OR: |
| return "M68kISD::OR"; |
| case M68kISD::XOR: |
| return "M68kISD::XOR"; |
| case M68kISD::AND: |
| return "M68kISD::AND"; |
| case M68kISD::CMP: |
| return "M68kISD::CMP"; |
| case M68kISD::BTST: |
| return "M68kISD::BTST"; |
| case M68kISD::SELECT: |
| return "M68kISD::SELECT"; |
| case M68kISD::CMOV: |
| return "M68kISD::CMOV"; |
| case M68kISD::BRCOND: |
| return "M68kISD::BRCOND"; |
| case M68kISD::SETCC: |
| return "M68kISD::SETCC"; |
| case M68kISD::SETCC_CARRY: |
| return "M68kISD::SETCC_CARRY"; |
| case M68kISD::GLOBAL_BASE_REG: |
| return "M68kISD::GLOBAL_BASE_REG"; |
| case M68kISD::Wrapper: |
| return "M68kISD::Wrapper"; |
| case M68kISD::WrapperPC: |
| return "M68kISD::WrapperPC"; |
| case M68kISD::SEG_ALLOCA: |
| return "M68kISD::SEG_ALLOCA"; |
| default: |
| return NULL; |
| } |
| } |
| |
| CCAssignFn *M68kTargetLowering::getCCAssignFn(CallingConv::ID CC, bool Return, |
| bool IsVarArg) const { |
| if (Return) |
| return RetCC_M68k_C; |
| else |
| return CC_M68k_C; |
| } |