768 lines
28 KiB
C++
768 lines
28 KiB
C++
|
//===- ARCISelLowering.cpp - ARC 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
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
//
|
||
|
// This file implements the ARCTargetLowering class.
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "ARCISelLowering.h"
|
||
|
#include "ARC.h"
|
||
|
#include "ARCMachineFunctionInfo.h"
|
||
|
#include "ARCSubtarget.h"
|
||
|
#include "ARCTargetMachine.h"
|
||
|
#include "MCTargetDesc/ARCInfo.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/ValueTypes.h"
|
||
|
#include "llvm/IR/CallingConv.h"
|
||
|
#include "llvm/IR/Intrinsics.h"
|
||
|
#include "llvm/Support/Debug.h"
|
||
|
#include <algorithm>
|
||
|
|
||
|
#define DEBUG_TYPE "arc-lower"
|
||
|
|
||
|
using namespace llvm;
|
||
|
|
||
|
static SDValue lowerCallResult(SDValue Chain, SDValue InFlag,
|
||
|
const SmallVectorImpl<CCValAssign> &RVLocs,
|
||
|
SDLoc dl, SelectionDAG &DAG,
|
||
|
SmallVectorImpl<SDValue> &InVals);
|
||
|
|
||
|
static ARCCC::CondCode ISDCCtoARCCC(ISD::CondCode isdCC) {
|
||
|
switch (isdCC) {
|
||
|
case ISD::SETUEQ:
|
||
|
return ARCCC::EQ;
|
||
|
case ISD::SETUGT:
|
||
|
return ARCCC::HI;
|
||
|
case ISD::SETUGE:
|
||
|
return ARCCC::HS;
|
||
|
case ISD::SETULT:
|
||
|
return ARCCC::LO;
|
||
|
case ISD::SETULE:
|
||
|
return ARCCC::LS;
|
||
|
case ISD::SETUNE:
|
||
|
return ARCCC::NE;
|
||
|
case ISD::SETEQ:
|
||
|
return ARCCC::EQ;
|
||
|
case ISD::SETGT:
|
||
|
return ARCCC::GT;
|
||
|
case ISD::SETGE:
|
||
|
return ARCCC::GE;
|
||
|
case ISD::SETLT:
|
||
|
return ARCCC::LT;
|
||
|
case ISD::SETLE:
|
||
|
return ARCCC::LE;
|
||
|
case ISD::SETNE:
|
||
|
return ARCCC::NE;
|
||
|
default:
|
||
|
llvm_unreachable("Unhandled ISDCC code.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ARCTargetLowering::ARCTargetLowering(const TargetMachine &TM,
|
||
|
const ARCSubtarget &Subtarget)
|
||
|
: TargetLowering(TM), Subtarget(Subtarget) {
|
||
|
// Set up the register classes.
|
||
|
addRegisterClass(MVT::i32, &ARC::GPR32RegClass);
|
||
|
|
||
|
// Compute derived properties from the register classes
|
||
|
computeRegisterProperties(Subtarget.getRegisterInfo());
|
||
|
|
||
|
setStackPointerRegisterToSaveRestore(ARC::SP);
|
||
|
|
||
|
setSchedulingPreference(Sched::Source);
|
||
|
|
||
|
// Use i32 for setcc operations results (slt, sgt, ...).
|
||
|
setBooleanContents(ZeroOrOneBooleanContent);
|
||
|
setBooleanVectorContents(ZeroOrOneBooleanContent);
|
||
|
|
||
|
for (unsigned Opc = 0; Opc < ISD::BUILTIN_OP_END; ++Opc)
|
||
|
setOperationAction(Opc, MVT::i32, Expand);
|
||
|
|
||
|
// Operations to get us off of the ground.
|
||
|
// Basic.
|
||
|
setOperationAction(ISD::ADD, MVT::i32, Legal);
|
||
|
setOperationAction(ISD::SUB, MVT::i32, Legal);
|
||
|
setOperationAction(ISD::AND, MVT::i32, Legal);
|
||
|
setOperationAction(ISD::SMAX, MVT::i32, Legal);
|
||
|
setOperationAction(ISD::SMIN, MVT::i32, Legal);
|
||
|
|
||
|
// Need barrel shifter.
|
||
|
setOperationAction(ISD::SHL, MVT::i32, Legal);
|
||
|
setOperationAction(ISD::SRA, MVT::i32, Legal);
|
||
|
setOperationAction(ISD::SRL, MVT::i32, Legal);
|
||
|
setOperationAction(ISD::ROTR, MVT::i32, Legal);
|
||
|
|
||
|
setOperationAction(ISD::Constant, MVT::i32, Legal);
|
||
|
setOperationAction(ISD::UNDEF, MVT::i32, Legal);
|
||
|
|
||
|
// Need multiplier
|
||
|
setOperationAction(ISD::MUL, MVT::i32, Legal);
|
||
|
setOperationAction(ISD::MULHS, MVT::i32, Legal);
|
||
|
setOperationAction(ISD::MULHU, MVT::i32, Legal);
|
||
|
setOperationAction(ISD::LOAD, MVT::i32, Legal);
|
||
|
setOperationAction(ISD::STORE, MVT::i32, Legal);
|
||
|
|
||
|
setOperationAction(ISD::SELECT_CC, MVT::i32, Custom);
|
||
|
setOperationAction(ISD::BR_CC, MVT::i32, Custom);
|
||
|
setOperationAction(ISD::BRCOND, MVT::Other, Expand);
|
||
|
setOperationAction(ISD::BR_JT, MVT::Other, Expand);
|
||
|
setOperationAction(ISD::JumpTable, MVT::i32, Custom);
|
||
|
|
||
|
// Have pseudo instruction for frame addresses.
|
||
|
setOperationAction(ISD::FRAMEADDR, MVT::i32, Legal);
|
||
|
// Custom lower global addresses.
|
||
|
setOperationAction(ISD::GlobalAddress, MVT::i32, Custom);
|
||
|
|
||
|
// Expand var-args ops.
|
||
|
setOperationAction(ISD::VASTART, MVT::Other, Custom);
|
||
|
setOperationAction(ISD::VAEND, MVT::Other, Expand);
|
||
|
setOperationAction(ISD::VAARG, MVT::Other, Expand);
|
||
|
setOperationAction(ISD::VACOPY, MVT::Other, Expand);
|
||
|
|
||
|
// Other expansions
|
||
|
setOperationAction(ISD::STACKSAVE, MVT::Other, Expand);
|
||
|
setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand);
|
||
|
|
||
|
// Sign extend inreg
|
||
|
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Custom);
|
||
|
}
|
||
|
|
||
|
const char *ARCTargetLowering::getTargetNodeName(unsigned Opcode) const {
|
||
|
switch (Opcode) {
|
||
|
case ARCISD::BL:
|
||
|
return "ARCISD::BL";
|
||
|
case ARCISD::CMOV:
|
||
|
return "ARCISD::CMOV";
|
||
|
case ARCISD::CMP:
|
||
|
return "ARCISD::CMP";
|
||
|
case ARCISD::BRcc:
|
||
|
return "ARCISD::BRcc";
|
||
|
case ARCISD::RET:
|
||
|
return "ARCISD::RET";
|
||
|
case ARCISD::GAWRAPPER:
|
||
|
return "ARCISD::GAWRAPPER";
|
||
|
}
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Misc Lower Operation implementation
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
SDValue ARCTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const {
|
||
|
SDValue LHS = Op.getOperand(0);
|
||
|
SDValue RHS = Op.getOperand(1);
|
||
|
ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(4))->get();
|
||
|
SDValue TVal = Op.getOperand(2);
|
||
|
SDValue FVal = Op.getOperand(3);
|
||
|
SDLoc dl(Op);
|
||
|
ARCCC::CondCode ArcCC = ISDCCtoARCCC(CC);
|
||
|
assert(LHS.getValueType() == MVT::i32 && "Only know how to SELECT_CC i32");
|
||
|
SDValue Cmp = DAG.getNode(ARCISD::CMP, dl, MVT::Glue, LHS, RHS);
|
||
|
return DAG.getNode(ARCISD::CMOV, dl, TVal.getValueType(), TVal, FVal,
|
||
|
DAG.getConstant(ArcCC, dl, MVT::i32), Cmp);
|
||
|
}
|
||
|
|
||
|
SDValue ARCTargetLowering::LowerSIGN_EXTEND_INREG(SDValue Op,
|
||
|
SelectionDAG &DAG) const {
|
||
|
SDValue Op0 = Op.getOperand(0);
|
||
|
SDLoc dl(Op);
|
||
|
assert(Op.getValueType() == MVT::i32 &&
|
||
|
"Unhandled target sign_extend_inreg.");
|
||
|
// These are legal
|
||
|
unsigned Width = cast<VTSDNode>(Op.getOperand(1))->getVT().getSizeInBits();
|
||
|
if (Width == 16 || Width == 8)
|
||
|
return Op;
|
||
|
if (Width >= 32) {
|
||
|
return {};
|
||
|
}
|
||
|
SDValue LS = DAG.getNode(ISD::SHL, dl, MVT::i32, Op0,
|
||
|
DAG.getConstant(32 - Width, dl, MVT::i32));
|
||
|
SDValue SR = DAG.getNode(ISD::SRA, dl, MVT::i32, LS,
|
||
|
DAG.getConstant(32 - Width, dl, MVT::i32));
|
||
|
return SR;
|
||
|
}
|
||
|
|
||
|
SDValue ARCTargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const {
|
||
|
SDValue Chain = Op.getOperand(0);
|
||
|
ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(1))->get();
|
||
|
SDValue LHS = Op.getOperand(2);
|
||
|
SDValue RHS = Op.getOperand(3);
|
||
|
SDValue Dest = Op.getOperand(4);
|
||
|
SDLoc dl(Op);
|
||
|
ARCCC::CondCode arcCC = ISDCCtoARCCC(CC);
|
||
|
assert(LHS.getValueType() == MVT::i32 && "Only know how to BR_CC i32");
|
||
|
return DAG.getNode(ARCISD::BRcc, dl, MVT::Other, Chain, Dest, LHS, RHS,
|
||
|
DAG.getConstant(arcCC, dl, MVT::i32));
|
||
|
}
|
||
|
|
||
|
SDValue ARCTargetLowering::LowerJumpTable(SDValue Op, SelectionDAG &DAG) const {
|
||
|
auto *N = cast<JumpTableSDNode>(Op);
|
||
|
SDValue GA = DAG.getTargetJumpTable(N->getIndex(), MVT::i32);
|
||
|
return DAG.getNode(ARCISD::GAWRAPPER, SDLoc(N), MVT::i32, GA);
|
||
|
}
|
||
|
|
||
|
#include "ARCGenCallingConv.inc"
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Call Calling Convention Implementation
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
/// ARC call implementation
|
||
|
SDValue ARCTargetLowering::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 IsVarArg = CLI.IsVarArg;
|
||
|
bool &IsTailCall = CLI.IsTailCall;
|
||
|
|
||
|
IsTailCall = false; // Do not support tail calls yet.
|
||
|
|
||
|
SmallVector<CCValAssign, 16> ArgLocs;
|
||
|
CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), ArgLocs,
|
||
|
*DAG.getContext());
|
||
|
|
||
|
CCInfo.AnalyzeCallOperands(Outs, CC_ARC);
|
||
|
|
||
|
SmallVector<CCValAssign, 16> RVLocs;
|
||
|
// Analyze return values to determine the number of bytes of stack required.
|
||
|
CCState RetCCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), RVLocs,
|
||
|
*DAG.getContext());
|
||
|
RetCCInfo.AllocateStack(CCInfo.getNextStackOffset(), Align(4));
|
||
|
RetCCInfo.AnalyzeCallResult(Ins, RetCC_ARC);
|
||
|
|
||
|
// Get a count of how many bytes are to be pushed on the stack.
|
||
|
unsigned NumBytes = RetCCInfo.getNextStackOffset();
|
||
|
auto PtrVT = getPointerTy(DAG.getDataLayout());
|
||
|
|
||
|
Chain = DAG.getCALLSEQ_START(Chain, NumBytes, 0, dl);
|
||
|
|
||
|
SmallVector<std::pair<unsigned, SDValue>, 4> RegsToPass;
|
||
|
SmallVector<SDValue, 12> MemOpChains;
|
||
|
|
||
|
SDValue StackPtr;
|
||
|
// Walk the register/memloc assignments, inserting copies/loads.
|
||
|
for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) {
|
||
|
CCValAssign &VA = ArgLocs[i];
|
||
|
SDValue Arg = OutVals[i];
|
||
|
|
||
|
// 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, VA.getLocVT(), Arg);
|
||
|
break;
|
||
|
case CCValAssign::ZExt:
|
||
|
Arg = DAG.getNode(ISD::ZERO_EXTEND, dl, VA.getLocVT(), Arg);
|
||
|
break;
|
||
|
case CCValAssign::AExt:
|
||
|
Arg = DAG.getNode(ISD::ANY_EXTEND, dl, VA.getLocVT(), Arg);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Arguments that can be passed on register must be kept at
|
||
|
// RegsToPass vector
|
||
|
if (VA.isRegLoc()) {
|
||
|
RegsToPass.push_back(std::make_pair(VA.getLocReg(), Arg));
|
||
|
} else {
|
||
|
assert(VA.isMemLoc() && "Must be register or memory argument.");
|
||
|
if (!StackPtr.getNode())
|
||
|
StackPtr = DAG.getCopyFromReg(Chain, dl, ARC::SP,
|
||
|
getPointerTy(DAG.getDataLayout()));
|
||
|
// Calculate the stack position.
|
||
|
SDValue SOffset = DAG.getIntPtrConstant(VA.getLocMemOffset(), dl);
|
||
|
SDValue PtrOff = DAG.getNode(
|
||
|
ISD::ADD, dl, getPointerTy(DAG.getDataLayout()), StackPtr, SOffset);
|
||
|
|
||
|
SDValue Store =
|
||
|
DAG.getStore(Chain, dl, Arg, PtrOff, MachinePointerInfo());
|
||
|
MemOpChains.push_back(Store);
|
||
|
IsTailCall = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Transform all store nodes into one single node because
|
||
|
// all store nodes are independent of each other.
|
||
|
if (!MemOpChains.empty())
|
||
|
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, MemOpChains);
|
||
|
|
||
|
// Build a sequence of copy-to-reg nodes chained together with token
|
||
|
// chain and flag operands which copy the outgoing args into registers.
|
||
|
// The InFlag in necessary since all emitted instructions must be
|
||
|
// stuck together.
|
||
|
SDValue Glue;
|
||
|
for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) {
|
||
|
Chain = DAG.getCopyToReg(Chain, dl, RegsToPass[i].first,
|
||
|
RegsToPass[i].second, Glue);
|
||
|
Glue = Chain.getValue(1);
|
||
|
}
|
||
|
|
||
|
// 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.
|
||
|
// Likewise ExternalSymbol -> TargetExternalSymbol.
|
||
|
bool IsDirect = true;
|
||
|
if (auto *G = dyn_cast<GlobalAddressSDNode>(Callee))
|
||
|
Callee = DAG.getTargetGlobalAddress(G->getGlobal(), dl, MVT::i32);
|
||
|
else if (auto *E = dyn_cast<ExternalSymbolSDNode>(Callee))
|
||
|
Callee = DAG.getTargetExternalSymbol(E->getSymbol(), MVT::i32);
|
||
|
else
|
||
|
IsDirect = false;
|
||
|
// Branch + Link = #chain, #target_address, #opt_in_flags...
|
||
|
// = Chain, Callee, Reg#1, Reg#2, ...
|
||
|
//
|
||
|
// Returns a chain & a flag for retval copy to use.
|
||
|
SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue);
|
||
|
SmallVector<SDValue, 8> Ops;
|
||
|
Ops.push_back(Chain);
|
||
|
Ops.push_back(Callee);
|
||
|
|
||
|
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 TargetRegisterInfo *TRI = Subtarget.getRegisterInfo();
|
||
|
const uint32_t *Mask =
|
||
|
TRI->getCallPreservedMask(DAG.getMachineFunction(), CallConv);
|
||
|
assert(Mask && "Missing call preserved mask for calling convention");
|
||
|
Ops.push_back(DAG.getRegisterMask(Mask));
|
||
|
|
||
|
if (Glue.getNode())
|
||
|
Ops.push_back(Glue);
|
||
|
|
||
|
Chain = DAG.getNode(IsDirect ? ARCISD::BL : ARCISD::JL, dl, NodeTys, Ops);
|
||
|
Glue = Chain.getValue(1);
|
||
|
|
||
|
// Create the CALLSEQ_END node.
|
||
|
Chain = DAG.getCALLSEQ_END(Chain, DAG.getConstant(NumBytes, dl, PtrVT, true),
|
||
|
DAG.getConstant(0, dl, PtrVT, true), Glue, dl);
|
||
|
Glue = Chain.getValue(1);
|
||
|
|
||
|
// Handle result values, copying them out of physregs into vregs that we
|
||
|
// return.
|
||
|
if (IsTailCall)
|
||
|
return Chain;
|
||
|
return lowerCallResult(Chain, Glue, RVLocs, dl, DAG, InVals);
|
||
|
}
|
||
|
|
||
|
/// Lower the result values of a call into the appropriate copies out of
|
||
|
/// physical registers / memory locations.
|
||
|
static SDValue lowerCallResult(SDValue Chain, SDValue Glue,
|
||
|
const SmallVectorImpl<CCValAssign> &RVLocs,
|
||
|
SDLoc dl, SelectionDAG &DAG,
|
||
|
SmallVectorImpl<SDValue> &InVals) {
|
||
|
SmallVector<std::pair<int, unsigned>, 4> ResultMemLocs;
|
||
|
// Copy results out of physical registers.
|
||
|
for (unsigned i = 0, e = RVLocs.size(); i != e; ++i) {
|
||
|
const CCValAssign &VA = RVLocs[i];
|
||
|
if (VA.isRegLoc()) {
|
||
|
SDValue RetValue;
|
||
|
RetValue =
|
||
|
DAG.getCopyFromReg(Chain, dl, VA.getLocReg(), VA.getValVT(), Glue);
|
||
|
Chain = RetValue.getValue(1);
|
||
|
Glue = RetValue.getValue(2);
|
||
|
InVals.push_back(RetValue);
|
||
|
} else {
|
||
|
assert(VA.isMemLoc() && "Must be memory location.");
|
||
|
ResultMemLocs.push_back(
|
||
|
std::make_pair(VA.getLocMemOffset(), InVals.size()));
|
||
|
|
||
|
// Reserve space for this result.
|
||
|
InVals.push_back(SDValue());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Copy results out of memory.
|
||
|
SmallVector<SDValue, 4> MemOpChains;
|
||
|
for (unsigned i = 0, e = ResultMemLocs.size(); i != e; ++i) {
|
||
|
int Offset = ResultMemLocs[i].first;
|
||
|
unsigned Index = ResultMemLocs[i].second;
|
||
|
SDValue StackPtr = DAG.getRegister(ARC::SP, MVT::i32);
|
||
|
SDValue SpLoc = DAG.getNode(ISD::ADD, dl, MVT::i32, StackPtr,
|
||
|
DAG.getConstant(Offset, dl, MVT::i32));
|
||
|
SDValue Load =
|
||
|
DAG.getLoad(MVT::i32, dl, Chain, SpLoc, MachinePointerInfo());
|
||
|
InVals[Index] = Load;
|
||
|
MemOpChains.push_back(Load.getValue(1));
|
||
|
}
|
||
|
|
||
|
// Transform all loads nodes into one single node because
|
||
|
// all load nodes are independent of each other.
|
||
|
if (!MemOpChains.empty())
|
||
|
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, MemOpChains);
|
||
|
|
||
|
return Chain;
|
||
|
}
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Formal Arguments Calling Convention Implementation
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
struct ArgDataPair {
|
||
|
SDValue SDV;
|
||
|
ISD::ArgFlagsTy Flags;
|
||
|
};
|
||
|
|
||
|
} // end anonymous namespace
|
||
|
|
||
|
/// ARC formal arguments implementation
|
||
|
SDValue ARCTargetLowering::LowerFormalArguments(
|
||
|
SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
|
||
|
const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &dl,
|
||
|
SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const {
|
||
|
switch (CallConv) {
|
||
|
default:
|
||
|
llvm_unreachable("Unsupported calling convention");
|
||
|
case CallingConv::C:
|
||
|
case CallingConv::Fast:
|
||
|
return LowerCallArguments(Chain, CallConv, IsVarArg, Ins, dl, DAG, InVals);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Transform physical registers into virtual registers, and generate load
|
||
|
/// operations for argument places on the stack.
|
||
|
SDValue ARCTargetLowering::LowerCallArguments(
|
||
|
SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
|
||
|
const SmallVectorImpl<ISD::InputArg> &Ins, SDLoc dl, SelectionDAG &DAG,
|
||
|
SmallVectorImpl<SDValue> &InVals) const {
|
||
|
MachineFunction &MF = DAG.getMachineFunction();
|
||
|
MachineFrameInfo &MFI = MF.getFrameInfo();
|
||
|
MachineRegisterInfo &RegInfo = MF.getRegInfo();
|
||
|
auto *AFI = MF.getInfo<ARCFunctionInfo>();
|
||
|
|
||
|
// Assign locations to all of the incoming arguments.
|
||
|
SmallVector<CCValAssign, 16> ArgLocs;
|
||
|
CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), ArgLocs,
|
||
|
*DAG.getContext());
|
||
|
|
||
|
CCInfo.AnalyzeFormalArguments(Ins, CC_ARC);
|
||
|
|
||
|
unsigned StackSlotSize = 4;
|
||
|
|
||
|
if (!IsVarArg)
|
||
|
AFI->setReturnStackOffset(CCInfo.getNextStackOffset());
|
||
|
|
||
|
// All getCopyFromReg ops must precede any getMemcpys to prevent the
|
||
|
// scheduler clobbering a register before it has been copied.
|
||
|
// The stages are:
|
||
|
// 1. CopyFromReg (and load) arg & vararg registers.
|
||
|
// 2. Chain CopyFromReg nodes into a TokenFactor.
|
||
|
// 3. Memcpy 'byVal' args & push final InVals.
|
||
|
// 4. Chain mem ops nodes into a TokenFactor.
|
||
|
SmallVector<SDValue, 4> CFRegNode;
|
||
|
SmallVector<ArgDataPair, 4> ArgData;
|
||
|
SmallVector<SDValue, 4> MemOps;
|
||
|
|
||
|
// 1a. CopyFromReg (and load) arg registers.
|
||
|
for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) {
|
||
|
CCValAssign &VA = ArgLocs[i];
|
||
|
SDValue ArgIn;
|
||
|
|
||
|
if (VA.isRegLoc()) {
|
||
|
// Arguments passed in registers
|
||
|
EVT RegVT = VA.getLocVT();
|
||
|
switch (RegVT.getSimpleVT().SimpleTy) {
|
||
|
default: {
|
||
|
LLVM_DEBUG(errs() << "LowerFormalArguments Unhandled argument type: "
|
||
|
<< (unsigned)RegVT.getSimpleVT().SimpleTy << "\n");
|
||
|
llvm_unreachable("Unhandled LowerFormalArguments type.");
|
||
|
}
|
||
|
case MVT::i32:
|
||
|
unsigned VReg = RegInfo.createVirtualRegister(&ARC::GPR32RegClass);
|
||
|
RegInfo.addLiveIn(VA.getLocReg(), VReg);
|
||
|
ArgIn = DAG.getCopyFromReg(Chain, dl, VReg, RegVT);
|
||
|
CFRegNode.push_back(ArgIn.getValue(ArgIn->getNumValues() - 1));
|
||
|
}
|
||
|
} else {
|
||
|
// sanity check
|
||
|
assert(VA.isMemLoc());
|
||
|
// Load the argument to a virtual register
|
||
|
unsigned ObjSize = VA.getLocVT().getStoreSize();
|
||
|
assert((ObjSize <= StackSlotSize) && "Unhandled argument");
|
||
|
|
||
|
// Create the frame index object for this incoming parameter...
|
||
|
int FI = MFI.CreateFixedObject(ObjSize, VA.getLocMemOffset(), true);
|
||
|
|
||
|
// Create the SelectionDAG nodes corresponding to a load
|
||
|
// from this parameter
|
||
|
SDValue FIN = DAG.getFrameIndex(FI, MVT::i32);
|
||
|
ArgIn = DAG.getLoad(VA.getLocVT(), dl, Chain, FIN,
|
||
|
MachinePointerInfo::getFixedStack(MF, FI));
|
||
|
}
|
||
|
const ArgDataPair ADP = {ArgIn, Ins[i].Flags};
|
||
|
ArgData.push_back(ADP);
|
||
|
}
|
||
|
|
||
|
// 1b. CopyFromReg vararg registers.
|
||
|
if (IsVarArg) {
|
||
|
// Argument registers
|
||
|
static const MCPhysReg ArgRegs[] = {ARC::R0, ARC::R1, ARC::R2, ARC::R3,
|
||
|
ARC::R4, ARC::R5, ARC::R6, ARC::R7};
|
||
|
auto *AFI = MF.getInfo<ARCFunctionInfo>();
|
||
|
unsigned FirstVAReg = CCInfo.getFirstUnallocated(ArgRegs);
|
||
|
if (FirstVAReg < array_lengthof(ArgRegs)) {
|
||
|
int Offset = 0;
|
||
|
// Save remaining registers, storing higher register numbers at a higher
|
||
|
// address
|
||
|
// There are (array_lengthof(ArgRegs) - FirstVAReg) registers which
|
||
|
// need to be saved.
|
||
|
int VarFI =
|
||
|
MFI.CreateFixedObject((array_lengthof(ArgRegs) - FirstVAReg) * 4,
|
||
|
CCInfo.getNextStackOffset(), true);
|
||
|
AFI->setVarArgsFrameIndex(VarFI);
|
||
|
SDValue FIN = DAG.getFrameIndex(VarFI, MVT::i32);
|
||
|
for (unsigned i = FirstVAReg; i < array_lengthof(ArgRegs); i++) {
|
||
|
// Move argument from phys reg -> virt reg
|
||
|
unsigned VReg = RegInfo.createVirtualRegister(&ARC::GPR32RegClass);
|
||
|
RegInfo.addLiveIn(ArgRegs[i], VReg);
|
||
|
SDValue Val = DAG.getCopyFromReg(Chain, dl, VReg, MVT::i32);
|
||
|
CFRegNode.push_back(Val.getValue(Val->getNumValues() - 1));
|
||
|
SDValue VAObj = DAG.getNode(ISD::ADD, dl, MVT::i32, FIN,
|
||
|
DAG.getConstant(Offset, dl, MVT::i32));
|
||
|
// Move argument from virt reg -> stack
|
||
|
SDValue Store =
|
||
|
DAG.getStore(Val.getValue(1), dl, Val, VAObj, MachinePointerInfo());
|
||
|
MemOps.push_back(Store);
|
||
|
Offset += 4;
|
||
|
}
|
||
|
} else {
|
||
|
llvm_unreachable("Too many var args parameters.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 2. Chain CopyFromReg nodes into a TokenFactor.
|
||
|
if (!CFRegNode.empty())
|
||
|
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, CFRegNode);
|
||
|
|
||
|
// 3. Memcpy 'byVal' args & push final InVals.
|
||
|
// Aggregates passed "byVal" need to be copied by the callee.
|
||
|
// The callee will use a pointer to this copy, rather than the original
|
||
|
// pointer.
|
||
|
for (const auto &ArgDI : ArgData) {
|
||
|
if (ArgDI.Flags.isByVal() && ArgDI.Flags.getByValSize()) {
|
||
|
unsigned Size = ArgDI.Flags.getByValSize();
|
||
|
Align Alignment =
|
||
|
std::max(Align(StackSlotSize), ArgDI.Flags.getNonZeroByValAlign());
|
||
|
// Create a new object on the stack and copy the pointee into it.
|
||
|
int FI = MFI.CreateStackObject(Size, Alignment, false);
|
||
|
SDValue FIN = DAG.getFrameIndex(FI, MVT::i32);
|
||
|
InVals.push_back(FIN);
|
||
|
MemOps.push_back(DAG.getMemcpy(
|
||
|
Chain, dl, FIN, ArgDI.SDV, DAG.getConstant(Size, dl, MVT::i32),
|
||
|
Alignment, false, false, false, MachinePointerInfo(),
|
||
|
MachinePointerInfo()));
|
||
|
} else {
|
||
|
InVals.push_back(ArgDI.SDV);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 4. Chain mem ops nodes into a TokenFactor.
|
||
|
if (!MemOps.empty()) {
|
||
|
MemOps.push_back(Chain);
|
||
|
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, MemOps);
|
||
|
}
|
||
|
|
||
|
return Chain;
|
||
|
}
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Return Value Calling Convention Implementation
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
bool ARCTargetLowering::CanLowerReturn(
|
||
|
CallingConv::ID CallConv, MachineFunction &MF, bool IsVarArg,
|
||
|
const SmallVectorImpl<ISD::OutputArg> &Outs, LLVMContext &Context) const {
|
||
|
SmallVector<CCValAssign, 16> RVLocs;
|
||
|
CCState CCInfo(CallConv, IsVarArg, MF, RVLocs, Context);
|
||
|
if (!CCInfo.CheckReturn(Outs, RetCC_ARC))
|
||
|
return false;
|
||
|
if (CCInfo.getNextStackOffset() != 0 && IsVarArg)
|
||
|
return false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
SDValue
|
||
|
ARCTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
|
||
|
bool IsVarArg,
|
||
|
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||
|
const SmallVectorImpl<SDValue> &OutVals,
|
||
|
const SDLoc &dl, SelectionDAG &DAG) const {
|
||
|
auto *AFI = DAG.getMachineFunction().getInfo<ARCFunctionInfo>();
|
||
|
MachineFrameInfo &MFI = DAG.getMachineFunction().getFrameInfo();
|
||
|
|
||
|
// CCValAssign - represent the assignment of
|
||
|
// the return value to a location
|
||
|
SmallVector<CCValAssign, 16> RVLocs;
|
||
|
|
||
|
// CCState - Info about the registers and stack slot.
|
||
|
CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), RVLocs,
|
||
|
*DAG.getContext());
|
||
|
|
||
|
// Analyze return values.
|
||
|
if (!IsVarArg)
|
||
|
CCInfo.AllocateStack(AFI->getReturnStackOffset(), Align(4));
|
||
|
|
||
|
CCInfo.AnalyzeReturn(Outs, RetCC_ARC);
|
||
|
|
||
|
SDValue Flag;
|
||
|
SmallVector<SDValue, 4> RetOps(1, Chain);
|
||
|
SmallVector<SDValue, 4> MemOpChains;
|
||
|
// Handle return values that must be copied to memory.
|
||
|
for (unsigned i = 0, e = RVLocs.size(); i != e; ++i) {
|
||
|
CCValAssign &VA = RVLocs[i];
|
||
|
if (VA.isRegLoc())
|
||
|
continue;
|
||
|
assert(VA.isMemLoc());
|
||
|
if (IsVarArg) {
|
||
|
report_fatal_error("Can't return value from vararg function in memory");
|
||
|
}
|
||
|
|
||
|
int Offset = VA.getLocMemOffset();
|
||
|
unsigned ObjSize = VA.getLocVT().getStoreSize();
|
||
|
// Create the frame index object for the memory location.
|
||
|
int FI = MFI.CreateFixedObject(ObjSize, Offset, false);
|
||
|
|
||
|
// Create a SelectionDAG node corresponding to a store
|
||
|
// to this memory location.
|
||
|
SDValue FIN = DAG.getFrameIndex(FI, MVT::i32);
|
||
|
MemOpChains.push_back(DAG.getStore(
|
||
|
Chain, dl, OutVals[i], FIN,
|
||
|
MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI)));
|
||
|
}
|
||
|
|
||
|
// Transform all store nodes into one single node because
|
||
|
// all stores are independent of each other.
|
||
|
if (!MemOpChains.empty())
|
||
|
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, MemOpChains);
|
||
|
|
||
|
// Now handle return values copied to registers.
|
||
|
for (unsigned i = 0, e = RVLocs.size(); i != e; ++i) {
|
||
|
CCValAssign &VA = RVLocs[i];
|
||
|
if (!VA.isRegLoc())
|
||
|
continue;
|
||
|
// Copy the result values into the output registers.
|
||
|
Chain = DAG.getCopyToReg(Chain, dl, VA.getLocReg(), OutVals[i], Flag);
|
||
|
|
||
|
// guarantee that all emitted copies are
|
||
|
// stuck together, avoiding something bad
|
||
|
Flag = Chain.getValue(1);
|
||
|
RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT()));
|
||
|
}
|
||
|
|
||
|
RetOps[0] = Chain; // Update chain.
|
||
|
|
||
|
// Add the flag if we have it.
|
||
|
if (Flag.getNode())
|
||
|
RetOps.push_back(Flag);
|
||
|
|
||
|
// What to do with the RetOps?
|
||
|
return DAG.getNode(ARCISD::RET, dl, MVT::Other, RetOps);
|
||
|
}
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Target Optimization Hooks
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
SDValue ARCTargetLowering::PerformDAGCombine(SDNode *N,
|
||
|
DAGCombinerInfo &DCI) const {
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Addressing mode description hooks
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
/// Return true if the addressing mode represented by AM is legal for this
|
||
|
/// target, for a load/store of the specified type.
|
||
|
bool ARCTargetLowering::isLegalAddressingMode(const DataLayout &DL,
|
||
|
const AddrMode &AM, Type *Ty,
|
||
|
unsigned AS,
|
||
|
Instruction *I) const {
|
||
|
return AM.Scale == 0;
|
||
|
}
|
||
|
|
||
|
// Don't emit tail calls for the time being.
|
||
|
bool ARCTargetLowering::mayBeEmittedAsTailCall(const CallInst *CI) const {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
SDValue ARCTargetLowering::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const {
|
||
|
const ARCRegisterInfo &ARI = *Subtarget.getRegisterInfo();
|
||
|
MachineFunction &MF = DAG.getMachineFunction();
|
||
|
MachineFrameInfo &MFI = MF.getFrameInfo();
|
||
|
MFI.setFrameAddressIsTaken(true);
|
||
|
|
||
|
EVT VT = Op.getValueType();
|
||
|
SDLoc dl(Op);
|
||
|
assert(cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue() == 0 &&
|
||
|
"Only support lowering frame addr of current frame.");
|
||
|
Register FrameReg = ARI.getFrameRegister(MF);
|
||
|
return DAG.getCopyFromReg(DAG.getEntryNode(), dl, FrameReg, VT);
|
||
|
}
|
||
|
|
||
|
SDValue ARCTargetLowering::LowerGlobalAddress(SDValue Op,
|
||
|
SelectionDAG &DAG) const {
|
||
|
const GlobalAddressSDNode *GN = cast<GlobalAddressSDNode>(Op);
|
||
|
const GlobalValue *GV = GN->getGlobal();
|
||
|
SDLoc dl(GN);
|
||
|
int64_t Offset = GN->getOffset();
|
||
|
SDValue GA = DAG.getTargetGlobalAddress(GV, dl, MVT::i32, Offset);
|
||
|
return DAG.getNode(ARCISD::GAWRAPPER, dl, MVT::i32, GA);
|
||
|
}
|
||
|
|
||
|
static SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) {
|
||
|
MachineFunction &MF = DAG.getMachineFunction();
|
||
|
auto *FuncInfo = MF.getInfo<ARCFunctionInfo>();
|
||
|
|
||
|
// vastart just stores the address of the VarArgsFrameIndex slot into the
|
||
|
// memory location argument.
|
||
|
SDLoc dl(Op);
|
||
|
EVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout());
|
||
|
SDValue FR = DAG.getFrameIndex(FuncInfo->getVarArgsFrameIndex(), PtrVT);
|
||
|
const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue();
|
||
|
return DAG.getStore(Op.getOperand(0), dl, FR, Op.getOperand(1),
|
||
|
MachinePointerInfo(SV));
|
||
|
}
|
||
|
|
||
|
SDValue ARCTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
|
||
|
switch (Op.getOpcode()) {
|
||
|
case ISD::GlobalAddress:
|
||
|
return LowerGlobalAddress(Op, DAG);
|
||
|
case ISD::FRAMEADDR:
|
||
|
return LowerFRAMEADDR(Op, DAG);
|
||
|
case ISD::SELECT_CC:
|
||
|
return LowerSELECT_CC(Op, DAG);
|
||
|
case ISD::BR_CC:
|
||
|
return LowerBR_CC(Op, DAG);
|
||
|
case ISD::SIGN_EXTEND_INREG:
|
||
|
return LowerSIGN_EXTEND_INREG(Op, DAG);
|
||
|
case ISD::JumpTable:
|
||
|
return LowerJumpTable(Op, DAG);
|
||
|
case ISD::VASTART:
|
||
|
return LowerVASTART(Op, DAG);
|
||
|
default:
|
||
|
llvm_unreachable("unimplemented operand");
|
||
|
}
|
||
|
}
|