mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-17 04:16:46 +00:00

Similar to previous migration done for other targets (PowerPC, X86, ARM, etc). In the future, relocation specifiers should be encoded as part of M68kMCExpr instead of MCSymbolRefExpr.
3709 lines
131 KiB
C++
3709 lines
131 KiB
C++
//===-- 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 "MCTargetDesc/M68kMCExpr.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,
|
|
/*CI=*/nullptr, std::nullopt, 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);
|
|
}
|
|
|
|
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, MVT::Other, Ops);
|
|
}
|
|
|
|
// Returns a chain & a flag for retval copy to use.
|
|
Chain = DAG.getNode(M68kISD::CALL, DL, {MVT::Other, MVT::Glue}, 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");
|
|
(void)LastVal;
|
|
|
|
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 Type *RetTy) 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 ? (unsigned)ISD::MUL : (unsigned)M68kISD::UMUL;
|
|
CC = M68k::COND_VS;
|
|
break;
|
|
case ISD::SMULO:
|
|
PromoteMULO(ISD::SIGN_EXTEND);
|
|
NoOverflow = VT != MVT::i32;
|
|
BaseOp = NoOverflow ? (unsigned)ISD::MUL : (unsigned)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->getUser();
|
|
unsigned UOpNo = UI->getOperandNo();
|
|
if (User->getOpcode() == ISD::TRUNCATE && User->hasOneUse()) {
|
|
// Look past truncate.
|
|
UOpNo = User->use_begin()->getOperandNo();
|
|
User = User->use_begin()->getUser();
|
|
}
|
|
|
|
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()->users())
|
|
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) {
|
|
SDValue Cmov =
|
|
DAG.getNode(M68kISD::CMOV, DL, T1.getValueType(), 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.
|
|
SDValue Ops[] = {Op2, Op1, CC, Cond};
|
|
return DAG.getNode(M68kISD::CMOV, DL, Op.getValueType(), 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()->user_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(), M68kMCExpr::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.getSignedTargetConstant(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.getSignedTargetConstant(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, /*TRI=*/nullptr))
|
|
return false;
|
|
if (mi.definesRegister(M68k::CCR, /*TRI=*/nullptr))
|
|
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, /*TRI=*/nullptr) &&
|
|
!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.getSignedConstant(-(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.getSignedConstant(-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.getSignedConstant(-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;
|
|
}
|