blob: e950b44341c922572054276b01e628156ae3471e [file] [log] [blame]
//===-- HexagonISelLowering.cpp - Hexagon DAG Lowering Implementation -----===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// This file implements the interfaces that Hexagon uses to lower LLVM code
// into a selection DAG.
#include "HexagonISelLowering.h"
#include "Hexagon.h"
#include "HexagonMachineFunctionInfo.h"
#include "HexagonRegisterInfo.h"
#include "HexagonSubtarget.h"
#include "HexagonTargetMachine.h"
#include "HexagonTargetObjectFile.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/CodeGen/CallingConvLower.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/RuntimeLibcalls.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/CodeGen/TargetCallingConv.h"
#include "llvm/CodeGen/ValueTypes.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IntrinsicsHexagon.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Value.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <utility>
using namespace llvm;
#define DEBUG_TYPE "hexagon-lowering"
static cl::opt<bool> EmitJumpTables("hexagon-emit-jump-tables",
cl::init(true), cl::Hidden,
cl::desc("Control jump table emission on Hexagon target"));
static cl::opt<bool>
EnableHexSDNodeSched("enable-hexagon-sdnode-sched", cl::Hidden,
cl::desc("Enable Hexagon SDNode scheduling"));
static cl::opt<bool> EnableFastMath("ffast-math", cl::Hidden,
cl::desc("Enable Fast Math processing"));
static cl::opt<int> MinimumJumpTables("minimum-jump-tables", cl::Hidden,
cl::desc("Set minimum jump tables"));
static cl::opt<int>
MaxStoresPerMemcpyCL("max-store-memcpy", cl::Hidden, cl::init(6),
cl::desc("Max #stores to inline memcpy"));
static cl::opt<int>
MaxStoresPerMemcpyOptSizeCL("max-store-memcpy-Os", cl::Hidden, cl::init(4),
cl::desc("Max #stores to inline memcpy"));
static cl::opt<int>
MaxStoresPerMemmoveCL("max-store-memmove", cl::Hidden, cl::init(6),
cl::desc("Max #stores to inline memmove"));
static cl::opt<int>
MaxStoresPerMemmoveOptSizeCL("max-store-memmove-Os", cl::Hidden,
cl::desc("Max #stores to inline memmove"));
static cl::opt<int>
MaxStoresPerMemsetCL("max-store-memset", cl::Hidden, cl::init(8),
cl::desc("Max #stores to inline memset"));
static cl::opt<int>
MaxStoresPerMemsetOptSizeCL("max-store-memset-Os", cl::Hidden, cl::init(4),
cl::desc("Max #stores to inline memset"));
static cl::opt<bool> AlignLoads("hexagon-align-loads",
cl::Hidden, cl::init(false),
cl::desc("Rewrite unaligned loads as a pair of aligned loads"));
static cl::opt<bool>
DisableArgsMinAlignment("hexagon-disable-args-min-alignment", cl::Hidden,
cl::desc("Disable minimum alignment of 1 for "
"arguments passed by value on stack"));
namespace {
class HexagonCCState : public CCState {
unsigned NumNamedVarArgParams = 0;
HexagonCCState(CallingConv::ID CC, bool IsVarArg, MachineFunction &MF,
SmallVectorImpl<CCValAssign> &locs, LLVMContext &C,
unsigned NumNamedArgs)
: CCState(CC, IsVarArg, MF, locs, C),
NumNamedVarArgParams(NumNamedArgs) {}
unsigned getNumNamedVarArgParams() const { return NumNamedVarArgParams; }
} // end anonymous namespace
// Implement calling convention for Hexagon.
static bool CC_SkipOdd(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
CCValAssign::LocInfo &LocInfo,
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
static const MCPhysReg ArgRegs[] = {
Hexagon::R0, Hexagon::R1, Hexagon::R2,
Hexagon::R3, Hexagon::R4, Hexagon::R5
const unsigned NumArgRegs = std::size(ArgRegs);
unsigned RegNum = State.getFirstUnallocated(ArgRegs);
// RegNum is an index into ArgRegs: skip a register if RegNum is odd.
if (RegNum != NumArgRegs && RegNum % 2 == 1)
// Always return false here, as this function only makes sure that the first
// unallocated register has an even register number and does not actually
// allocate a register for the current argument.
return false;
#include ""
HexagonTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG)
const {
return SDValue();
/// CreateCopyOfByValArgument - Make a copy of an aggregate at address specified
/// by "Src" to address "Dst" of size "Size". Alignment information is
/// specified by the specific parameter attribute. The copy will be passed as
/// a byval function parameter. Sometimes what we are copying is the end of a
/// larger object, the part that does not fit in registers.
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=*/false,
/*isTailCall=*/false, MachinePointerInfo(), MachinePointerInfo());
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 (MF.getSubtarget<HexagonSubtarget>().useHVXOps())
return CCInfo.CheckReturn(Outs, RetCC_Hexagon_HVX);
return CCInfo.CheckReturn(Outs, RetCC_Hexagon);
// LowerReturn - Lower ISD::RET. If a struct is larger than 8 bytes and is
// passed by value, the function prototype is modified to return void and
// the value is stored in memory pointed by a pointer passed by caller.
HexagonTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
bool IsVarArg,
const SmallVectorImpl<ISD::OutputArg> &Outs,
const SmallVectorImpl<SDValue> &OutVals,
const SDLoc &dl, SelectionDAG &DAG) const {
// CCValAssign - represent the assignment of the return value to locations.
SmallVector<CCValAssign, 16> RVLocs;
// CCState - Info about the registers and stack slot.
CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), RVLocs,
// Analyze return values of ISD::RET
if (Subtarget.useHVXOps())
CCInfo.AnalyzeReturn(Outs, RetCC_Hexagon_HVX);
CCInfo.AnalyzeReturn(Outs, RetCC_Hexagon);
SDValue Glue;
SmallVector<SDValue, 4> RetOps(1, Chain);
// Copy the result values into the output registers.
for (unsigned i = 0; i != RVLocs.size(); ++i) {
CCValAssign &VA = RVLocs[i];
SDValue Val = OutVals[i];
switch (VA.getLocInfo()) {
// Loc info must be one of Full, BCvt, SExt, ZExt, or AExt.
llvm_unreachable("Unknown loc info!");
case CCValAssign::Full:
case CCValAssign::BCvt:
Val = DAG.getBitcast(VA.getLocVT(), Val);
case CCValAssign::SExt:
Val = DAG.getNode(ISD::SIGN_EXTEND, dl, VA.getLocVT(), Val);
case CCValAssign::ZExt:
Val = DAG.getNode(ISD::ZERO_EXTEND, dl, VA.getLocVT(), Val);
case CCValAssign::AExt:
Val = DAG.getNode(ISD::ANY_EXTEND, dl, VA.getLocVT(), Val);
Chain = DAG.getCopyToReg(Chain, dl, VA.getLocReg(), Val, Glue);
// Guarantee that all emitted copies are stuck together with flags.
Glue = Chain.getValue(1);
RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT()));
RetOps[0] = Chain; // Update chain.
// Add the glue if we have it.
if (Glue.getNode())
return DAG.getNode(HexagonISD::RET_GLUE, dl, MVT::Other, RetOps);
bool HexagonTargetLowering::mayBeEmittedAsTailCall(const CallInst *CI) const {
// If either no tail call or told not to tail call at all, don't.
return CI->isTailCall();
Register HexagonTargetLowering::getRegisterByName(
const char* RegName, LLT VT, const MachineFunction &) const {
// Just support r19, the linux kernel uses it.
Register Reg = StringSwitch<Register>(RegName)
.Case("r0", Hexagon::R0)
.Case("r1", Hexagon::R1)
.Case("r2", Hexagon::R2)
.Case("r3", Hexagon::R3)
.Case("r4", Hexagon::R4)
.Case("r5", Hexagon::R5)
.Case("r6", Hexagon::R6)
.Case("r7", Hexagon::R7)
.Case("r8", Hexagon::R8)
.Case("r9", Hexagon::R9)
.Case("r10", Hexagon::R10)
.Case("r11", Hexagon::R11)
.Case("r12", Hexagon::R12)
.Case("r13", Hexagon::R13)
.Case("r14", Hexagon::R14)
.Case("r15", Hexagon::R15)
.Case("r16", Hexagon::R16)
.Case("r17", Hexagon::R17)
.Case("r18", Hexagon::R18)
.Case("r19", Hexagon::R19)
.Case("r20", Hexagon::R20)
.Case("r21", Hexagon::R21)
.Case("r22", Hexagon::R22)
.Case("r23", Hexagon::R23)
.Case("r24", Hexagon::R24)
.Case("r25", Hexagon::R25)
.Case("r26", Hexagon::R26)
.Case("r27", Hexagon::R27)
.Case("r28", Hexagon::R28)
.Case("r29", Hexagon::R29)
.Case("r30", Hexagon::R30)
.Case("r31", Hexagon::R31)
.Case("r1:0", Hexagon::D0)
.Case("r3:2", Hexagon::D1)
.Case("r5:4", Hexagon::D2)
.Case("r7:6", Hexagon::D3)
.Case("r9:8", Hexagon::D4)
.Case("r11:10", Hexagon::D5)
.Case("r13:12", Hexagon::D6)
.Case("r15:14", Hexagon::D7)
.Case("r17:16", Hexagon::D8)
.Case("r19:18", Hexagon::D9)
.Case("r21:20", Hexagon::D10)
.Case("r23:22", Hexagon::D11)
.Case("r25:24", Hexagon::D12)
.Case("r27:26", Hexagon::D13)
.Case("r29:28", Hexagon::D14)
.Case("r31:30", Hexagon::D15)
.Case("sp", Hexagon::R29)
.Case("fp", Hexagon::R30)
.Case("lr", Hexagon::R31)
.Case("p0", Hexagon::P0)
.Case("p1", Hexagon::P1)
.Case("p2", Hexagon::P2)
.Case("p3", Hexagon::P3)
.Case("sa0", Hexagon::SA0)
.Case("lc0", Hexagon::LC0)
.Case("sa1", Hexagon::SA1)
.Case("lc1", Hexagon::LC1)
.Case("m0", Hexagon::M0)
.Case("m1", Hexagon::M1)
.Case("usr", Hexagon::USR)
.Case("ugp", Hexagon::UGP)
.Case("cs0", Hexagon::CS0)
.Case("cs1", Hexagon::CS1)
if (Reg)
return Reg;
report_fatal_error("Invalid register name global variable");
/// LowerCallResult - Lower the result values of an ISD::CALL into the
/// appropriate copies out of appropriate physical registers. This assumes that
/// Chain/Glue are the input chain/glue to use, and that TheCall is the call
/// being lowered. Returns a SDNode with the same number of values as the
/// ISD::CALL.
SDValue HexagonTargetLowering::LowerCallResult(
SDValue Chain, SDValue Glue, CallingConv::ID CallConv, bool IsVarArg,
const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &dl,
SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals,
const SmallVectorImpl<SDValue> &OutVals, SDValue Callee) const {
// Assign locations to each value returned by this call.
SmallVector<CCValAssign, 16> RVLocs;
CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), RVLocs,
if (Subtarget.useHVXOps())
CCInfo.AnalyzeCallResult(Ins, RetCC_Hexagon_HVX);
CCInfo.AnalyzeCallResult(Ins, RetCC_Hexagon);
// Copy all of the result registers out of their specified physreg.
for (unsigned i = 0; i != RVLocs.size(); ++i) {
SDValue RetVal;
if (RVLocs[i].getValVT() == MVT::i1) {
// Return values of type MVT::i1 require special handling. The reason
// is that MVT::i1 is associated with the PredRegs register class, but
// values of that type are still returned in R0. Generate an explicit
// copy into a predicate register from R0, and treat the value of the
// predicate register as the call result.
auto &MRI = DAG.getMachineFunction().getRegInfo();
SDValue FR0 = DAG.getCopyFromReg(Chain, dl, RVLocs[i].getLocReg(),
MVT::i32, Glue);
// FR0 = (Value, Chain, Glue)
Register PredR = MRI.createVirtualRegister(&Hexagon::PredRegsRegClass);
SDValue TPR = DAG.getCopyToReg(FR0.getValue(1), dl, PredR,
FR0.getValue(0), FR0.getValue(2));
// TPR = (Chain, Glue)
// Don't glue this CopyFromReg, because it copies from a virtual
// register. If it is glued to the call, InstrEmitter will add it
// as an implicit def to the call (EmitMachineNode).
RetVal = DAG.getCopyFromReg(TPR.getValue(0), dl, PredR, MVT::i1);
Glue = TPR.getValue(1);
Chain = TPR.getValue(0);
} else {
RetVal = DAG.getCopyFromReg(Chain, dl, RVLocs[i].getLocReg(),
RVLocs[i].getValVT(), Glue);
Glue = RetVal.getValue(2);
Chain = RetVal.getValue(1);
return Chain;
/// LowerCall - Functions arguments are copied from virtual regs to
/// (physical regs)/(stack frame), CALLSEQ_START and CALLSEQ_END are emitted.
HexagonTargetLowering::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 DoesNotReturn = CLI.DoesNotReturn;
bool IsStructRet = Outs.empty() ? false : Outs[0].Flags.isSRet();
MachineFunction &MF = DAG.getMachineFunction();
MachineFrameInfo &MFI = MF.getFrameInfo();
auto PtrVT = getPointerTy(MF.getDataLayout());
unsigned NumParams = CLI.CB ? CLI.CB->getFunctionType()->getNumParams() : 0;
if (GlobalAddressSDNode *GAN = dyn_cast<GlobalAddressSDNode>(Callee))
Callee = DAG.getTargetGlobalAddress(GAN->getGlobal(), dl, MVT::i32);
// Linux ABI treats var-arg calls the same way as regular ones.
bool TreatAsVarArg = !Subtarget.isEnvironmentMusl() && IsVarArg;
// Analyze operands of the call, assigning locations to each operand.
SmallVector<CCValAssign, 16> ArgLocs;
HexagonCCState CCInfo(CallConv, TreatAsVarArg, MF, ArgLocs, *DAG.getContext(),
if (Subtarget.useHVXOps())
CCInfo.AnalyzeCallOperands(Outs, CC_Hexagon_HVX);
else if (DisableArgsMinAlignment)
CCInfo.AnalyzeCallOperands(Outs, CC_Hexagon_Legacy);
CCInfo.AnalyzeCallOperands(Outs, CC_Hexagon);
if (CLI.IsTailCall) {
bool StructAttrFlag = MF.getFunction().hasStructRetAttr();
CLI.IsTailCall = IsEligibleForTailCallOptimization(Callee, CallConv,
IsVarArg, IsStructRet, StructAttrFlag, Outs,
OutVals, Ins, DAG);
for (const CCValAssign &VA : ArgLocs) {
if (VA.isMemLoc()) {
CLI.IsTailCall = false;
LLVM_DEBUG(dbgs() << (CLI.IsTailCall ? "Eligible for Tail Call\n"
: "Argument must be passed on stack. "
"Not eligible for Tail Call\n"));
// Get a count of how many bytes are to be pushed on the stack.
unsigned NumBytes = CCInfo.getStackSize();
SmallVector<std::pair<unsigned, SDValue>, 16> RegsToPass;
SmallVector<SDValue, 8> MemOpChains;
const HexagonRegisterInfo &HRI = *Subtarget.getRegisterInfo();
SDValue StackPtr =
DAG.getCopyFromReg(Chain, dl, HRI.getStackRegister(), PtrVT);
bool NeedsArgAlign = false;
Align LargestAlignSeen;
// 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];
ISD::ArgFlagsTy Flags = Outs[i].Flags;
// Record if we need > 8 byte alignment on an argument.
bool ArgAlign = Subtarget.isHVXVectorType(VA.getValVT());
NeedsArgAlign |= ArgAlign;
// Promote the value if needed.
switch (VA.getLocInfo()) {
// Loc info must be one of Full, BCvt, SExt, ZExt, or AExt.
llvm_unreachable("Unknown loc info!");
case CCValAssign::Full:
case CCValAssign::BCvt:
Arg = DAG.getBitcast(VA.getLocVT(), Arg);
case CCValAssign::SExt:
Arg = DAG.getNode(ISD::SIGN_EXTEND, dl, VA.getLocVT(), Arg);
case CCValAssign::ZExt:
Arg = DAG.getNode(ISD::ZERO_EXTEND, dl, VA.getLocVT(), Arg);
case CCValAssign::AExt:
Arg = DAG.getNode(ISD::ANY_EXTEND, dl, VA.getLocVT(), Arg);
if (VA.isMemLoc()) {
unsigned LocMemOffset = VA.getLocMemOffset();
SDValue MemAddr = DAG.getConstant(LocMemOffset, dl,
MemAddr = DAG.getNode(ISD::ADD, dl, MVT::i32, StackPtr, MemAddr);
if (ArgAlign)
LargestAlignSeen = std::max(
LargestAlignSeen, Align(VA.getLocVT().getStoreSizeInBits() / 8));
if (Flags.isByVal()) {
// The argument is a struct passed by value. According to LLVM, "Arg"
// is a pointer.
MemOpChains.push_back(CreateCopyOfByValArgument(Arg, MemAddr, Chain,
Flags, DAG, dl));
} else {
MachinePointerInfo LocPI = MachinePointerInfo::getStack(
DAG.getMachineFunction(), LocMemOffset);
SDValue S = DAG.getStore(Chain, dl, Arg, MemAddr, LocPI);
// 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));
if (NeedsArgAlign && Subtarget.hasV60Ops()) {
LLVM_DEBUG(dbgs() << "Function needs byte stack align due to call args\n");
Align VecAlign = HRI.getSpillAlign(Hexagon::HvxVRRegClass);
LargestAlignSeen = std::max(LargestAlignSeen, VecAlign);
// 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);
SDValue Glue;
if (!CLI.IsTailCall) {
Chain = DAG.getCALLSEQ_START(Chain, NumBytes, 0, dl);
Glue = Chain.getValue(1);
// Build a sequence of copy-to-reg nodes chained together with token
// chain and flag operands which copy the outgoing args into registers.
// The Glue is necessary since all emitted instructions must be
// stuck together.
if (!CLI.IsTailCall) {
for (const auto &R : RegsToPass) {
Chain = DAG.getCopyToReg(Chain, dl, R.first, R.second, Glue);
Glue = Chain.getValue(1);
} else {
// For tail calls lower the arguments to the 'real' stack slot.
// 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.
// Do not flag preceding copytoreg stuff together with the following stuff.
Glue = SDValue();
for (const auto &R : RegsToPass) {
Chain = DAG.getCopyToReg(Chain, dl, R.first, R.second, Glue);
Glue = Chain.getValue(1);
Glue = SDValue();
bool LongCalls = MF.getSubtarget<HexagonSubtarget>().useLongCalls();
unsigned Flags = LongCalls ? HexagonII::HMOTF_ConstExtended : 0;
// If the callee is a GlobalAddress/ExternalSymbol node (quite common, every
// direct call is) turn it into a TargetGlobalAddress/TargetExternalSymbol
// node so that legalize doesn't hack it.
if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) {
Callee = DAG.getTargetGlobalAddress(G->getGlobal(), dl, PtrVT, 0, Flags);
} else if (ExternalSymbolSDNode *S =
dyn_cast<ExternalSymbolSDNode>(Callee)) {
Callee = DAG.getTargetExternalSymbol(S->getSymbol(), PtrVT, Flags);
// Returns a chain & a flag for retval copy to use.
SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue);
SmallVector<SDValue, 8> Ops;
// Add argument registers to the end of the list so that they are
// known live into the call.
for (const auto &R : RegsToPass)
Ops.push_back(DAG.getRegister(R.first, R.second.getValueType()));
const uint32_t *Mask = HRI.getCallPreservedMask(MF, CallConv);
assert(Mask && "Missing call preserved mask for calling convention");
if (Glue.getNode())
if (CLI.IsTailCall) {
return DAG.getNode(HexagonISD::TC_RETURN, dl, NodeTys, Ops);
// Set this here because we need to know this for "hasFP" in frame lowering.
// The target-independent code calls getFrameRegister before setting it, and
// getFrameRegister uses hasFP to determine whether the function has FP.
unsigned OpCode = DoesNotReturn ? HexagonISD::CALLnr : HexagonISD::CALL;
Chain = DAG.getNode(OpCode, dl, NodeTys, Ops);
Glue = Chain.getValue(1);
// Create the CALLSEQ_END node.
Chain = DAG.getCALLSEQ_END(Chain, NumBytes, 0, Glue, dl);
Glue = Chain.getValue(1);
// Handle result values, copying them out of physregs into vregs that we
// return.
return LowerCallResult(Chain, Glue, CallConv, IsVarArg, Ins, dl, DAG,
InVals, OutVals, Callee);
/// Returns true by value, base pointer and offset pointer and addressing
/// mode by reference if this node can be combined with a load / store to
/// form a post-indexed load / store.
bool HexagonTargetLowering::getPostIndexedAddressParts(SDNode *N, SDNode *Op,
SDValue &Base, SDValue &Offset, ISD::MemIndexedMode &AM,
SelectionDAG &DAG) const {
LSBaseSDNode *LSN = dyn_cast<LSBaseSDNode>(N);
if (!LSN)
return false;
EVT VT = LSN->getMemoryVT();
if (!VT.isSimple())
return false;
bool IsLegalType = VT == MVT::i8 || VT == MVT::i16 || VT == MVT::i32 ||
VT == MVT::i64 || VT == MVT::f32 || VT == MVT::f64 ||
VT == MVT::v2i16 || VT == MVT::v2i32 || VT == MVT::v4i8 ||
VT == MVT::v4i16 || VT == MVT::v8i8 ||
if (!IsLegalType)
return false;
if (Op->getOpcode() != ISD::ADD)
return false;
Base = Op->getOperand(0);
Offset = Op->getOperand(1);
if (!isa<ConstantSDNode>(Offset.getNode()))
return false;
int32_t V = cast<ConstantSDNode>(Offset.getNode())->getSExtValue();
return Subtarget.getInstrInfo()->isValidAutoIncImm(VT, V);
HexagonTargetLowering::LowerINLINEASM(SDValue Op, SelectionDAG &DAG) const {
MachineFunction &MF = DAG.getMachineFunction();
auto &HMFI = *MF.getInfo<HexagonMachineFunctionInfo>();
const HexagonRegisterInfo &HRI = *Subtarget.getRegisterInfo();
unsigned LR = HRI.getRARegister();
if ((Op.getOpcode() != ISD::INLINEASM &&
Op.getOpcode() != ISD::INLINEASM_BR) || HMFI.hasClobberLR())
return Op;
unsigned NumOps = Op.getNumOperands();
if (Op.getOperand(NumOps-1).getValueType() == MVT::Glue)
--NumOps; // Ignore the flag operand.
for (unsigned i = InlineAsm::Op_FirstOperand; i != NumOps;) {
const InlineAsm::Flag Flags(
unsigned NumVals = Flags.getNumOperandRegisters();
++i; // Skip the ID value.
switch (Flags.getKind()) {
llvm_unreachable("Bad flags!");
case InlineAsm::Kind::RegUse:
case InlineAsm::Kind::Imm:
case InlineAsm::Kind::Mem:
i += NumVals;
case InlineAsm::Kind::Clobber:
case InlineAsm::Kind::RegDef:
case InlineAsm::Kind::RegDefEarlyClobber: {
for (; NumVals; --NumVals, ++i) {
Register Reg = cast<RegisterSDNode>(Op.getOperand(i))->getReg();
if (Reg != LR)
return Op;
return Op;
// Need to transform ISD::PREFETCH into something that doesn't inherit
// all of the properties of ISD::PREFETCH, specifically SDNPMayLoad and
// SDNPMayStore.
SDValue HexagonTargetLowering::LowerPREFETCH(SDValue Op,
SelectionDAG &DAG) const {
SDValue Chain = Op.getOperand(0);
SDValue Addr = Op.getOperand(1);
// Lower it to DCFETCH($reg, #0). A "pat" will try to merge the offset in,
// if the "reg" is fed by an "add".
SDLoc DL(Op);
SDValue Zero = DAG.getConstant(0, DL, MVT::i32);
return DAG.getNode(HexagonISD::DCFETCH, DL, MVT::Other, Chain, Addr, Zero);
// Custom-handle ISD::READCYCLECOUNTER because the target-independent SDNode
// is marked as having side-effects, while the register read on Hexagon does
// not have any. TableGen refuses to accept the direct pattern from that node
// to the A4_tfrcpp.
SDValue HexagonTargetLowering::LowerREADCYCLECOUNTER(SDValue Op,
SelectionDAG &DAG) const {
SDValue Chain = Op.getOperand(0);
SDLoc dl(Op);
SDVTList VTs = DAG.getVTList(MVT::i64, MVT::Other);
return DAG.getNode(HexagonISD::READCYCLE, dl, VTs, Chain);
SDValue HexagonTargetLowering::LowerINTRINSIC_VOID(SDValue Op,
SelectionDAG &DAG) const {
SDValue Chain = Op.getOperand(0);
unsigned IntNo = cast<ConstantSDNode>(Op.getOperand(1))->getZExtValue();
// Lower the hexagon_prefetch builtin to DCFETCH, as above.
if (IntNo == Intrinsic::hexagon_prefetch) {
SDValue Addr = Op.getOperand(2);
SDLoc DL(Op);
SDValue Zero = DAG.getConstant(0, DL, MVT::i32);
return DAG.getNode(HexagonISD::DCFETCH, DL, MVT::Other, Chain, Addr, Zero);
return SDValue();
HexagonTargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op,
SelectionDAG &DAG) const {
SDValue Chain = Op.getOperand(0);
SDValue Size = Op.getOperand(1);
SDValue Align = Op.getOperand(2);
SDLoc dl(Op);
ConstantSDNode *AlignConst = dyn_cast<ConstantSDNode>(Align);
assert(AlignConst && "Non-constant Align in LowerDYNAMIC_STACKALLOC");
unsigned A = AlignConst->getSExtValue();
auto &HFI = *Subtarget.getFrameLowering();
// "Zero" means natural stack alignment.
if (A == 0)
A = HFI.getStackAlign().value();
dbgs () << __func__ << " Align: " << A << " Size: ";
dbgs() << "\n";
SDValue AC = DAG.getConstant(A, dl, MVT::i32);
SDVTList VTs = DAG.getVTList(MVT::i32, MVT::Other);
SDValue AA = DAG.getNode(HexagonISD::ALLOCA, dl, VTs, Chain, Size, AC);
DAG.ReplaceAllUsesOfValueWith(Op, AA);
return AA;
SDValue HexagonTargetLowering::LowerFormalArguments(
SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &dl,
SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const {
MachineFunction &MF = DAG.getMachineFunction();
MachineFrameInfo &MFI = MF.getFrameInfo();
MachineRegisterInfo &MRI = MF.getRegInfo();
// Linux ABI treats var-arg calls the same way as regular ones.
bool TreatAsVarArg = !Subtarget.isEnvironmentMusl() && IsVarArg;
// Assign locations to all of the incoming arguments.
SmallVector<CCValAssign, 16> ArgLocs;
HexagonCCState CCInfo(CallConv, TreatAsVarArg, MF, ArgLocs,
if (Subtarget.useHVXOps())
CCInfo.AnalyzeFormalArguments(Ins, CC_Hexagon_HVX);
else if (DisableArgsMinAlignment)
CCInfo.AnalyzeFormalArguments(Ins, CC_Hexagon_Legacy);
CCInfo.AnalyzeFormalArguments(Ins, CC_Hexagon);
// For LLVM, in the case when returning a struct by value (>8byte),
// the first argument is a pointer that points to the location on caller's
// stack where the return value will be stored. For Hexagon, the location on
// caller's stack is passed only when the struct size is smaller than (and
// equal to) 8 bytes. If not, no address will be passed into callee and
// callee return the result direclty through R0/R1.
auto NextSingleReg = [] (const TargetRegisterClass &RC, unsigned Reg) {
switch (RC.getID()) {
case Hexagon::IntRegsRegClassID:
return Reg - Hexagon::R0 + 1;
case Hexagon::DoubleRegsRegClassID:
return (Reg - Hexagon::D0 + 1) * 2;
case Hexagon::HvxVRRegClassID:
return Reg - Hexagon::V0 + 1;
case Hexagon::HvxWRRegClassID:
return (Reg - Hexagon::W0 + 1) * 2;
llvm_unreachable("Unexpected register class");
auto &HFL = const_cast<HexagonFrameLowering&>(*Subtarget.getFrameLowering());
auto &HMFI = *MF.getInfo<HexagonMachineFunctionInfo>();
HFL.FirstVarArgSavedReg = 0;
for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) {
CCValAssign &VA = ArgLocs[i];
ISD::ArgFlagsTy Flags = Ins[i].Flags;
bool ByVal = Flags.isByVal();
// Arguments passed in registers:
// 1. 32- and 64-bit values and HVX vectors are passed directly,
// 2. Large structs are passed via an address, and the address is
// passed in a register.
if (VA.isRegLoc() && ByVal && Flags.getByValSize() <= 8)
llvm_unreachable("ByValSize must be bigger than 8 bytes");
bool InReg = VA.isRegLoc() &&
(!ByVal || (ByVal && Flags.getByValSize() > 8));
if (InReg) {
MVT RegVT = VA.getLocVT();
if (VA.getLocInfo() == CCValAssign::BCvt)
RegVT = VA.getValVT();
const TargetRegisterClass *RC = getRegClassFor(RegVT);
Register VReg = MRI.createVirtualRegister(RC);
SDValue Copy = DAG.getCopyFromReg(Chain, dl, VReg, RegVT);
// Treat values of type MVT::i1 specially: they are passed in
// registers of type i32, but they need to remain as values of
// type i1 for consistency of the argument lowering.
if (VA.getValVT() == MVT::i1) {
assert(RegVT.getSizeInBits() <= 32);
SDValue T = DAG.getNode(ISD::AND, dl, RegVT,
Copy, DAG.getConstant(1, dl, RegVT));
Copy = DAG.getSetCC(dl, MVT::i1, T, DAG.getConstant(0, dl, RegVT),
} else {
#ifndef NDEBUG
unsigned RegSize = RegVT.getSizeInBits();
assert(RegSize == 32 || RegSize == 64 ||
MRI.addLiveIn(VA.getLocReg(), VReg);
HFL.FirstVarArgSavedReg = NextSingleReg(*RC, VA.getLocReg());
} else {
assert(VA.isMemLoc() && "Argument should be passed in memory");
// If it's a byval parameter, then we need to compute the
// "real" size, not the size of the pointer.
unsigned ObjSize = Flags.isByVal()
? Flags.getByValSize()
: VA.getLocVT().getStoreSizeInBits() / 8;
// Create the frame index object for this incoming parameter.
int Offset = HEXAGON_LRFP_SIZE + VA.getLocMemOffset();
int FI = MFI.CreateFixedObject(ObjSize, Offset, true);
SDValue FIN = DAG.getFrameIndex(FI, MVT::i32);
if (Flags.isByVal()) {
// If it's a pass-by-value aggregate, then do not dereference the stack
// location. Instead, we should generate a reference to the stack
// location.
} else {
SDValue L = DAG.getLoad(VA.getValVT(), dl, Chain, FIN,
MachinePointerInfo::getFixedStack(MF, FI, 0));
if (IsVarArg && Subtarget.isEnvironmentMusl()) {
for (int i = HFL.FirstVarArgSavedReg; i < 6; i++)
if (IsVarArg && Subtarget.isEnvironmentMusl()) {
HMFI.setFirstNamedArgFrameIndex(HMFI.getFirstNamedArgFrameIndex() - 1);
// Create Frame index for the start of register saved area.
int NumVarArgRegs = 6 - HFL.FirstVarArgSavedReg;
bool RequiresPadding = (NumVarArgRegs & 1);
int RegSaveAreaSizePlusPadding = RequiresPadding
? (NumVarArgRegs + 1) * 4
: NumVarArgRegs * 4;
if (RegSaveAreaSizePlusPadding > 0) {
// The offset to saved register area should be 8 byte aligned.
int RegAreaStart = HEXAGON_LRFP_SIZE + CCInfo.getStackSize();
if (!(RegAreaStart % 8))
RegAreaStart = (RegAreaStart + 7) & -8;
int RegSaveAreaFrameIndex =
MFI.CreateFixedObject(RegSaveAreaSizePlusPadding, RegAreaStart, true);
// This will point to the next argument passed via stack.
int Offset = RegAreaStart + RegSaveAreaSizePlusPadding;
int FI = MFI.CreateFixedObject(Hexagon_PointerSize, Offset, true);
} else {
// This will point to the next argument passed via stack, when
// there is no saved register area.
int Offset = HEXAGON_LRFP_SIZE + CCInfo.getStackSize();
int FI = MFI.CreateFixedObject(Hexagon_PointerSize, Offset, true);
if (IsVarArg && !Subtarget.isEnvironmentMusl()) {
// This will point to the next argument passed via stack.
int Offset = HEXAGON_LRFP_SIZE + CCInfo.getStackSize();
int FI = MFI.CreateFixedObject(Hexagon_PointerSize, Offset, true);
return Chain;
HexagonTargetLowering::LowerVASTART(SDValue Op, SelectionDAG &DAG) const {
// VASTART stores the address of the VarArgsFrameIndex slot into the
// memory location argument.
MachineFunction &MF = DAG.getMachineFunction();
HexagonMachineFunctionInfo *QFI = MF.getInfo<HexagonMachineFunctionInfo>();
SDValue Addr = DAG.getFrameIndex(QFI->getVarArgsFrameIndex(), MVT::i32);
const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue();
if (!Subtarget.isEnvironmentMusl()) {
return DAG.getStore(Op.getOperand(0), SDLoc(Op), Addr, Op.getOperand(1),
auto &FuncInfo = *MF.getInfo<HexagonMachineFunctionInfo>();
auto &HFL = *Subtarget.getFrameLowering();
SDLoc DL(Op);
SmallVector<SDValue, 8> MemOps;
// Get frame index of va_list.
SDValue FIN = Op.getOperand(1);
// If first Vararg register is odd, add 4 bytes to start of
// saved register area to point to the first register location.
// This is because the saved register area has to be 8 byte aligned.
// Incase of an odd start register, there will be 4 bytes of padding in
// the beginning of saved register area. If all registers area used up,
// the following condition will handle it correctly.
SDValue SavedRegAreaStartFrameIndex =
DAG.getFrameIndex(FuncInfo.getRegSavedAreaStartFrameIndex(), MVT::i32);
auto PtrVT = getPointerTy(DAG.getDataLayout());
if (HFL.FirstVarArgSavedReg & 1)
SavedRegAreaStartFrameIndex =
DAG.getNode(ISD::ADD, DL, PtrVT,
DAG.getIntPtrConstant(4, DL));
// Store the saved register area start pointer.
SDValue Store =
DAG.getStore(Op.getOperand(0), DL,
FIN, MachinePointerInfo(SV));
// Store saved register area end pointer.
FIN = DAG.getNode(ISD::ADD, DL, PtrVT,
FIN, DAG.getIntPtrConstant(4, DL));
Store = DAG.getStore(Op.getOperand(0), DL,
FIN, MachinePointerInfo(SV, 4));
// Store overflow area pointer.
FIN = DAG.getNode(ISD::ADD, DL, PtrVT,
FIN, DAG.getIntPtrConstant(4, DL));
Store = DAG.getStore(Op.getOperand(0), DL,
FIN, MachinePointerInfo(SV, 8));
return DAG.getNode(ISD::TokenFactor, DL, MVT::Other, MemOps);
HexagonTargetLowering::LowerVACOPY(SDValue Op, SelectionDAG &DAG) const {
// Assert that the linux ABI is enabled for the current compilation.
assert(Subtarget.isEnvironmentMusl() && "Linux ABI should be enabled");
SDValue Chain = Op.getOperand(0);
SDValue DestPtr = Op.getOperand(1);
SDValue SrcPtr = Op.getOperand(2);
const Value *DestSV = cast<SrcValueSDNode>(Op.getOperand(3))->getValue();
const Value *SrcSV = cast<SrcValueSDNode>(Op.getOperand(4))->getValue();
SDLoc DL(Op);
// Size of the va_list is 12 bytes as it has 3 pointers. Therefore,
// we need to memcopy 12 bytes from va_list to another similar list.
return DAG.getMemcpy(Chain, DL, DestPtr, SrcPtr,
DAG.getIntPtrConstant(12, DL), Align(4),
/*isVolatile*/ false, false, false,
MachinePointerInfo(DestSV), MachinePointerInfo(SrcSV));
SDValue HexagonTargetLowering::LowerSETCC(SDValue Op, SelectionDAG &DAG) const {
const SDLoc &dl(Op);
SDValue LHS = Op.getOperand(0);
SDValue RHS = Op.getOperand(1);
ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(2))->get();
MVT ResTy = ty(Op);
MVT OpTy = ty(LHS);
if (OpTy == MVT::v2i16 || OpTy == MVT::v4i8) {
MVT ElemTy = OpTy.getVectorElementType();
MVT WideTy = MVT::getVectorVT(MVT::getIntegerVT(2*ElemTy.getSizeInBits()),
return DAG.getSetCC(dl, ResTy,
DAG.getSExtOrTrunc(LHS, SDLoc(LHS), WideTy),
DAG.getSExtOrTrunc(RHS, SDLoc(RHS), WideTy), CC);
// Treat all other vector types as legal.
if (ResTy.isVector())
return Op;
// Comparisons of short integers should use sign-extend, not zero-extend,
// since we can represent small negative values in the compare instructions.
// The LLVM default is to use zero-extend arbitrarily in these cases.
auto isSExtFree = [this](SDValue N) {
switch (N.getOpcode()) {
// A sign-extend of a truncate of a sign-extend is free.
SDValue Op = N.getOperand(0);
if (Op.getOpcode() != ISD::AssertSext)
return false;
EVT OrigTy = cast<VTSDNode>(Op.getOperand(1))->getVT();
unsigned ThisBW = ty(N).getSizeInBits();
unsigned OrigBW = OrigTy.getSizeInBits();
// The type that was sign-extended to get the AssertSext must be
// narrower than the type of N (so that N has still the same value
// as the original).
return ThisBW >= OrigBW;
case ISD::LOAD:
// We have sign-extended loads.
return true;
return false;
if (OpTy == MVT::i8 || OpTy == MVT::i16) {
ConstantSDNode *C = dyn_cast<ConstantSDNode>(RHS);
bool IsNegative = C && C->getAPIntValue().isNegative();
if (IsNegative || isSExtFree(LHS) || isSExtFree(RHS))
return DAG.getSetCC(dl, ResTy,
DAG.getSExtOrTrunc(LHS, SDLoc(LHS), MVT::i32),
DAG.getSExtOrTrunc(RHS, SDLoc(RHS), MVT::i32), CC);
return SDValue();
HexagonTargetLowering::LowerVSELECT(SDValue Op, SelectionDAG &DAG) const {
SDValue PredOp = Op.getOperand(0);
SDValue Op1 = Op.getOperand(1), Op2 = Op.getOperand(2);
MVT OpTy = ty(Op1);
const SDLoc &dl(Op);
if (OpTy == MVT::v2i16 || OpTy == MVT::v4i8) {
MVT ElemTy = OpTy.getVectorElementType();
MVT WideTy = MVT::getVectorVT(MVT::getIntegerVT(2*ElemTy.getSizeInBits()),
// Generate (trunc (select (_, sext, sext))).
return DAG.getSExtOrTrunc(
DAG.getSelect(dl, WideTy, PredOp,
DAG.getSExtOrTrunc(Op1, dl, WideTy),
DAG.getSExtOrTrunc(Op2, dl, WideTy)),
dl, OpTy);
return SDValue();
HexagonTargetLowering::LowerConstantPool(SDValue Op, SelectionDAG &DAG) const {
EVT ValTy = Op.getValueType();
ConstantPoolSDNode *CPN = cast<ConstantPoolSDNode>(Op);
Constant *CVal = nullptr;
bool isVTi1Type = false;
if (auto *CV = dyn_cast<ConstantVector>(CPN->getConstVal())) {
if (cast<VectorType>(CV->getType())->getElementType()->isIntegerTy(1)) {
IRBuilder<> IRB(CV->getContext());
SmallVector<Constant*, 128> NewConst;
unsigned VecLen = CV->getNumOperands();
assert(isPowerOf2_32(VecLen) &&
"conversion only supported for pow2 VectorSize");
for (unsigned i = 0; i < VecLen; ++i)
CVal = ConstantVector::get(NewConst);
isVTi1Type = true;
Align Alignment = CPN->getAlign();
bool IsPositionIndependent = isPositionIndependent();
unsigned char TF = IsPositionIndependent ? HexagonII::MO_PCREL : 0;
unsigned Offset = 0;
SDValue T;
if (CPN->isMachineConstantPoolEntry())
T = DAG.getTargetConstantPool(CPN->getMachineCPVal(), ValTy, Alignment,
Offset, TF);
else if (isVTi1Type)
T = DAG.getTargetConstantPool(CVal, ValTy, Alignment, Offset, TF);
T = DAG.getTargetConstantPool(CPN->getConstVal(), ValTy, Alignment, Offset,
assert(cast<ConstantPoolSDNode>(T)->getTargetFlags() == TF &&
"Inconsistent target flag encountered");
if (IsPositionIndependent)
return DAG.getNode(HexagonISD::AT_PCREL, SDLoc(Op), ValTy, T);
return DAG.getNode(HexagonISD::CP, SDLoc(Op), ValTy, T);
HexagonTargetLowering::LowerJumpTable(SDValue Op, SelectionDAG &DAG) const {
EVT VT = Op.getValueType();
int Idx = cast<JumpTableSDNode>(Op)->getIndex();
if (isPositionIndependent()) {
SDValue T = DAG.getTargetJumpTable(Idx, VT, HexagonII::MO_PCREL);
return DAG.getNode(HexagonISD::AT_PCREL, SDLoc(Op), VT, T);
SDValue T = DAG.getTargetJumpTable(Idx, VT);
return DAG.getNode(HexagonISD::JT, SDLoc(Op), VT, T);
HexagonTargetLowering::LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const {
const HexagonRegisterInfo &HRI = *Subtarget.getRegisterInfo();
MachineFunction &MF = DAG.getMachineFunction();
MachineFrameInfo &MFI = MF.getFrameInfo();
if (verifyReturnAddressArgumentIsConstant(Op, DAG))
return SDValue();
EVT VT = Op.getValueType();
SDLoc dl(Op);
unsigned Depth = cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue();
if (Depth) {
SDValue FrameAddr = LowerFRAMEADDR(Op, DAG);
SDValue Offset = DAG.getConstant(4, dl, MVT::i32);
return DAG.getLoad(VT, dl, DAG.getEntryNode(),
DAG.getNode(ISD::ADD, dl, VT, FrameAddr, Offset),
// Return LR, which contains the return address. Mark it an implicit live-in.
Register Reg = MF.addLiveIn(HRI.getRARegister(), getRegClassFor(MVT::i32));
return DAG.getCopyFromReg(DAG.getEntryNode(), dl, Reg, VT);
HexagonTargetLowering::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const {
const HexagonRegisterInfo &HRI = *Subtarget.getRegisterInfo();
MachineFrameInfo &MFI = DAG.getMachineFunction().getFrameInfo();
EVT VT = Op.getValueType();
SDLoc dl(Op);
unsigned Depth = cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue();
SDValue FrameAddr = DAG.getCopyFromReg(DAG.getEntryNode(), dl,
HRI.getFrameRegister(), VT);
while (Depth--)
FrameAddr = DAG.getLoad(VT, dl, DAG.getEntryNode(), FrameAddr,
return FrameAddr;
HexagonTargetLowering::LowerATOMIC_FENCE(SDValue Op, SelectionDAG& DAG) const {
SDLoc dl(Op);
return DAG.getNode(HexagonISD::BARRIER, dl, MVT::Other, Op.getOperand(0));
HexagonTargetLowering::LowerGLOBALADDRESS(SDValue Op, SelectionDAG &DAG) const {
SDLoc dl(Op);
auto *GAN = cast<GlobalAddressSDNode>(Op);
auto PtrVT = getPointerTy(DAG.getDataLayout());
auto *GV = GAN->getGlobal();
int64_t Offset = GAN->getOffset();
auto &HLOF = *HTM.getObjFileLowering();
Reloc::Model RM = HTM.getRelocationModel();
if (RM == Reloc::Static) {
SDValue GA = DAG.getTargetGlobalAddress(GV, dl, PtrVT, Offset);
const GlobalObject *GO = GV->getAliaseeObject();
if (GO && Subtarget.useSmallData() && HLOF.isGlobalInSmallSection(GO, HTM))
return DAG.getNode(HexagonISD::CONST32_GP, dl, PtrVT, GA);
return DAG.getNode(HexagonISD::CONST32, dl, PtrVT, GA);
bool UsePCRel = getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV);
if (UsePCRel) {
SDValue GA = DAG.getTargetGlobalAddress(GV, dl, PtrVT, Offset,
return DAG.getNode(HexagonISD::AT_PCREL, dl, PtrVT, GA);
// Use GOT index.
SDValue GA = DAG.getTargetGlobalAddress(GV, dl, PtrVT, 0, HexagonII::MO_GOT);
SDValue Off = DAG.getConstant(Offset, dl, MVT::i32);
return DAG.getNode(HexagonISD::AT_GOT, dl, PtrVT, GOT, GA, Off);
// Specifies that for loads and stores VT can be promoted to PromotedLdStVT.
HexagonTargetLowering::LowerBlockAddress(SDValue Op, SelectionDAG &DAG) const {
const BlockAddress *BA = cast<BlockAddressSDNode>(Op)->getBlockAddress();
SDLoc dl(Op);
EVT PtrVT = getPointerTy(DAG.getDataLayout());
Reloc::Model RM = HTM.getRelocationModel();
if (RM == Reloc::Static) {
SDValue A = DAG.getTargetBlockAddress(BA, PtrVT);
return DAG.getNode(HexagonISD::CONST32_GP, dl, PtrVT, A);
SDValue A = DAG.getTargetBlockAddress(BA, PtrVT, 0, HexagonII::MO_PCREL);
return DAG.getNode(HexagonISD::AT_PCREL, dl, PtrVT, A);
HexagonTargetLowering::LowerGLOBAL_OFFSET_TABLE(SDValue Op, SelectionDAG &DAG)
const {
EVT PtrVT = getPointerTy(DAG.getDataLayout());
SDValue GOTSym = DAG.getTargetExternalSymbol(HEXAGON_GOT_SYM_NAME, PtrVT,
return DAG.getNode(HexagonISD::AT_PCREL, SDLoc(Op), PtrVT, GOTSym);
HexagonTargetLowering::GetDynamicTLSAddr(SelectionDAG &DAG, SDValue Chain,
GlobalAddressSDNode *GA, SDValue Glue, EVT PtrVT, unsigned ReturnReg,
unsigned char OperandFlags) const {
MachineFunction &MF = DAG.getMachineFunction();
MachineFrameInfo &MFI = MF.getFrameInfo();
SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue);
SDLoc dl(GA);
SDValue TGA = DAG.getTargetGlobalAddress(GA->getGlobal(), dl,
// Create Operands for the call.The Operands should have the following:
// 1. Chain SDValue
// 2. Callee which in this case is the Global address value.
// 3. Registers live into the call.In this case its R0, as we
// have just one argument to be passed.
// 4. Glue.
// Note: The order is important.
const auto &HRI = *Subtarget.getRegisterInfo();
const uint32_t *Mask = HRI.getCallPreservedMask(MF, CallingConv::C);
assert(Mask && "Missing call preserved mask for calling convention");
SDValue Ops[] = { Chain, TGA, DAG.getRegister(Hexagon::R0, PtrVT),
DAG.getRegisterMask(Mask), Glue };
Chain = DAG.getNode(HexagonISD::CALL, dl, NodeTys, Ops);
// Inform MFI that function has calls.
Glue = Chain.getValue(1);
return DAG.getCopyFromReg(Chain, dl, ReturnReg, PtrVT, Glue);
// Lower using the intial executable model for TLS addresses
HexagonTargetLowering::LowerToTLSInitialExecModel(GlobalAddressSDNode *GA,
SelectionDAG &DAG) const {
SDLoc dl(GA);
int64_t Offset = GA->getOffset();
auto PtrVT = getPointerTy(DAG.getDataLayout());
// Get the thread pointer.
SDValue TP = DAG.getCopyFromReg(DAG.getEntryNode(), dl, Hexagon::UGP, PtrVT);
bool IsPositionIndependent = isPositionIndependent();
unsigned char TF =
IsPositionIndependent ? HexagonII::MO_IEGOT : HexagonII::MO_IE;
// First generate the TLS symbol address
SDValue TGA = DAG.getTargetGlobalAddress(GA->getGlobal(), dl, PtrVT,
Offset, TF);
SDValue Sym = DAG.getNode(HexagonISD::CONST32, dl, PtrVT, TGA);
if (IsPositionIndependent) {
// Generate the GOT pointer in case of position independent code
// Add the TLS Symbol address to GOT pointer.This gives
// GOT relative relocation for the symbol.
Sym = DAG.getNode(ISD::ADD, dl, PtrVT, GOT, Sym);
// Load the offset value for TLS symbol.This offset is relative to
// thread pointer.
SDValue LoadOffset =
DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), Sym, MachinePointerInfo());
// Address of the thread local variable is the add of thread
// pointer and the offset of the variable.
return DAG.getNode(ISD::ADD, dl, PtrVT, TP, LoadOffset);
// Lower using the local executable model for TLS addresses
HexagonTargetLowering::LowerToTLSLocalExecModel(GlobalAddressSDNode *GA,
SelectionDAG &DAG) const {
SDLoc dl(GA);
int64_t Offset = GA->getOffset();
auto PtrVT = getPointerTy(DAG.getDataLayout());
// Get the thread pointer.
SDValue TP = DAG.getCopyFromReg(DAG.getEntryNode(), dl, Hexagon::UGP, PtrVT);
// Generate the TLS symbol address
SDValue TGA = DAG.getTargetGlobalAddress(GA->getGlobal(), dl, PtrVT, Offset,
SDValue Sym = DAG.getNode(HexagonISD::CONST32, dl, PtrVT, TGA);
// Address of the thread local variable is the add of thread
// pointer and the offset of the variable.
return DAG.getNode(ISD::ADD, dl, PtrVT, TP, Sym);
// Lower using the general dynamic model for TLS addresses
HexagonTargetLowering::LowerToTLSGeneralDynamicModel(GlobalAddressSDNode *GA,
SelectionDAG &DAG) const {
SDLoc dl(GA);
int64_t Offset = GA->getOffset();
auto PtrVT = getPointerTy(DAG.getDataLayout());
// First generate the TLS symbol address
SDValue TGA = DAG.getTargetGlobalAddress(GA->getGlobal(), dl, PtrVT, Offset,
// Then, generate the GOT pointer
// Add the TLS symbol and the GOT pointer
SDValue Sym = DAG.getNode(HexagonISD::CONST32, dl, PtrVT, TGA);
SDValue Chain = DAG.getNode(ISD::ADD, dl, PtrVT, GOT, Sym);
// Copy over the argument to R0
SDValue InGlue;
Chain = DAG.getCopyToReg(DAG.getEntryNode(), dl, Hexagon::R0, Chain, InGlue);
InGlue = Chain.getValue(1);
unsigned Flags = DAG.getSubtarget<HexagonSubtarget>().useLongCalls()
? HexagonII::MO_GDPLT | HexagonII::HMOTF_ConstExtended
: HexagonII::MO_GDPLT;
return GetDynamicTLSAddr(DAG, Chain, GA, InGlue, PtrVT,
Hexagon::R0, Flags);
// Lower TLS addresses.
// For now for dynamic models, we only support the general dynamic model.
HexagonTargetLowering::LowerGlobalTLSAddress(SDValue Op,
SelectionDAG &DAG) const {
GlobalAddressSDNode *GA = cast<GlobalAddressSDNode>(Op);
switch (HTM.getTLSModel(GA->getGlobal())) {
case TLSModel::GeneralDynamic:
case TLSModel::LocalDynamic:
return LowerToTLSGeneralDynamicModel(GA, DAG);
case TLSModel::InitialExec:
return LowerToTLSInitialExecModel(GA, DAG);
case TLSModel::LocalExec:
return LowerToTLSLocalExecModel(GA, DAG);
llvm_unreachable("Bogus TLS model");
// TargetLowering Implementation
HexagonTargetLowering::HexagonTargetLowering(const TargetMachine &TM,
const HexagonSubtarget &ST)
: TargetLowering(TM), HTM(static_cast<const HexagonTargetMachine&>(TM)),
Subtarget(ST) {
auto &HRI = *Subtarget.getRegisterInfo();
if (EnableHexSDNodeSched)
// Limits for inline expansion of memcpy/memmove
MaxStoresPerMemcpy = MaxStoresPerMemcpyCL;
MaxStoresPerMemcpyOptSize = MaxStoresPerMemcpyOptSizeCL;
MaxStoresPerMemmove = MaxStoresPerMemmoveCL;
MaxStoresPerMemmoveOptSize = MaxStoresPerMemmoveOptSizeCL;
MaxStoresPerMemset = MaxStoresPerMemsetCL;
MaxStoresPerMemsetOptSize = MaxStoresPerMemsetOptSizeCL;
// Set up register classes.
addRegisterClass(MVT::i1, &Hexagon::PredRegsRegClass);
addRegisterClass(MVT::v2i1, &Hexagon::PredRegsRegClass); // bbbbaaaa
addRegisterClass(MVT::v4i1, &Hexagon::PredRegsRegClass); // ddccbbaa
addRegisterClass(MVT::v8i1, &Hexagon::PredRegsRegClass); // hgfedcba
addRegisterClass(MVT::i32, &Hexagon::IntRegsRegClass);
addRegisterClass(MVT::v2i16, &Hexagon::IntRegsRegClass);
addRegisterClass(MVT::v4i8, &Hexagon::IntRegsRegClass);
addRegisterClass(MVT::i64, &Hexagon::DoubleRegsRegClass);
addRegisterClass(MVT::v8i8, &Hexagon::DoubleRegsRegClass);
addRegisterClass(MVT::v4i16, &Hexagon::DoubleRegsRegClass);
addRegisterClass(MVT::v2i32, &Hexagon::DoubleRegsRegClass);
addRegisterClass(MVT::f32, &Hexagon::IntRegsRegClass);
addRegisterClass(MVT::f64, &Hexagon::DoubleRegsRegClass);
// Handling of scalar operations.
// All operations default to "legal", except:
// - indexed loads and stores (pre-/post-incremented),
// which default to "expand" for at least one type.
// Misc operations.
setOperationAction(ISD::ConstantFP, MVT::f32, Legal);
setOperationAction(ISD::ConstantFP, MVT::f64, Legal);
setOperationAction(ISD::TRAP, MVT::Other, Legal);
setOperationAction(ISD::ConstantPool, MVT::i32, Custom);
setOperationAction(ISD::JumpTable, MVT::i32, Custom);
setOperationAction(ISD::BUILD_PAIR, MVT::i64, Expand);
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand);
setOperationAction(ISD::INLINEASM, MVT::Other, Custom);
setOperationAction(ISD::INLINEASM_BR, MVT::Other, Custom);
setOperationAction(ISD::PREFETCH, MVT::Other, Custom);
setOperationAction(ISD::READCYCLECOUNTER, MVT::i64, Custom);
setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom);
setOperationAction(ISD::EH_RETURN, MVT::Other, Custom);
setOperationAction(ISD::GLOBAL_OFFSET_TABLE, MVT::i32, Custom);
setOperationAction(ISD::GlobalTLSAddress, MVT::i32, Custom);
setOperationAction(ISD::ATOMIC_FENCE, MVT::Other, Custom);
// Custom legalize GlobalAddress nodes into CONST32.
setOperationAction(ISD::GlobalAddress, MVT::i32, Custom);
setOperationAction(ISD::GlobalAddress, MVT::i8, Custom);
setOperationAction(ISD::BlockAddress, MVT::i32, Custom);
// Hexagon needs to optimize cases with negative constants.
setOperationAction(ISD::SETCC, MVT::i8, Custom);
setOperationAction(ISD::SETCC, MVT::i16, Custom);
setOperationAction(ISD::SETCC, MVT::v4i8, Custom);
setOperationAction(ISD::SETCC, MVT::v2i16, Custom);
// VASTART needs to be custom lowered to use the VarArgsFrameIndex.
setOperationAction(ISD::VASTART, MVT::Other, Custom);
setOperationAction(ISD::VAEND, MVT::Other, Expand);
setOperationAction(ISD::VAARG, MVT::Other, Expand);
if (Subtarget.isEnvironmentMusl())
setOperationAction(ISD::VACOPY, MVT::Other, Custom);
setOperationAction(ISD::VACOPY, MVT::Other, Expand);
setOperationAction(ISD::STACKSAVE, MVT::Other, Expand);
setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand);
setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i32, Custom);
if (EmitJumpTables)
setOperationAction(ISD::BR_JT, MVT::Other, Expand);
for (unsigned LegalIntOp :
setOperationAction(LegalIntOp, MVT::i32, Legal);
setOperationAction(LegalIntOp, MVT::i64, Legal);
// Hexagon has A4_addp_c and A4_subp_c that take and generate a carry bit,
// but they only operate on i64.
for (MVT VT : MVT::integer_valuetypes()) {
setOperationAction(ISD::UADDO, VT, Custom);
setOperationAction(ISD::USUBO, VT, Custom);
setOperationAction(ISD::SADDO, VT, Expand);
setOperationAction(ISD::SSUBO, VT, Expand);
setOperationAction(ISD::UADDO_CARRY, VT, Expand);
setOperationAction(ISD::USUBO_CARRY, VT, Expand);
setOperationAction(ISD::UADDO_CARRY, MVT::i64, Custom);
setOperationAction(ISD::USUBO_CARRY, MVT::i64, Custom);
setOperationAction(ISD::CTLZ, MVT::i8, Promote);
setOperationAction(ISD::CTLZ, MVT::i16, Promote);
setOperationAction(ISD::CTTZ, MVT::i8, Promote);
setOperationAction(ISD::CTTZ, MVT::i16, Promote);
// Popcount can count # of 1s in i64 but returns i32.
setOperationAction(ISD::CTPOP, MVT::i8, Promote);
setOperationAction(ISD::CTPOP, MVT::i16, Promote);
setOperationAction(ISD::CTPOP, MVT::i32, Promote);
setOperationAction(ISD::CTPOP, MVT::i64, Legal);
setOperationAction(ISD::BITREVERSE, MVT::i32, Legal);
setOperationAction(ISD::BITREVERSE, MVT::i64, Legal);
setOperationAction(ISD::BSWAP, MVT::i32, Legal);
setOperationAction(ISD::BSWAP, MVT::i64, Legal);
setOperationAction(ISD::FSHL, MVT::i32, Legal);
setOperationAction(ISD::FSHL, MVT::i64, Legal);
setOperationAction(ISD::FSHR, MVT::i32, Legal);
setOperationAction(ISD::FSHR, MVT::i64, Legal);
for (unsigned IntExpOp :
for (MVT VT : MVT::integer_valuetypes())
setOperationAction(IntExpOp, VT, Expand);
for (unsigned FPExpOp :
for (MVT VT : MVT::fp_valuetypes())
setOperationAction(FPExpOp, VT, Expand);
// No extending loads from i32.
for (MVT VT : MVT::integer_valuetypes()) {
setLoadExtAction(ISD::ZEXTLOAD, VT, MVT::i32, Expand);
setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i32, Expand);
setLoadExtAction(ISD::EXTLOAD, VT, MVT::i32, Expand);
// Turn FP truncstore into trunc + store.
setTruncStoreAction(MVT::f64, MVT::f32, Expand);
// Turn FP extload into load/fpextend.
for (MVT VT : MVT::fp_valuetypes())
setLoadExtAction(ISD::EXTLOAD, VT, MVT::f32, Expand);
// Expand BR_CC and SELECT_CC for all integer and fp types.
for (MVT VT : MVT::integer_valuetypes()) {
setOperationAction(ISD::BR_CC, VT, Expand);
setOperationAction(ISD::SELECT_CC, VT, Expand);
for (MVT VT : MVT::fp_valuetypes()) {
setOperationAction(ISD::BR_CC, VT, Expand);
setOperationAction(ISD::SELECT_CC, VT, Expand);
setOperationAction(ISD::BR_CC, MVT::Other, Expand);
// Handling of vector operations.
// Set the action for vector operations to "expand", then override it with
// either "custom" or "legal" for specific cases.
static const unsigned VectExpOps[] = {
// Integer arithmetic:
// Logical/bit:
// Floating point arithmetic/math functions:
// Misc:
ISD::BR_CC, ISD::SELECT_CC, ISD::ConstantPool,
// Vector:
for (MVT VT : MVT::fixedlen_vector_valuetypes()) {
for (unsigned VectExpOp : VectExpOps)
setOperationAction(VectExpOp, VT, Expand);
// Expand all extending loads and truncating stores:
for (MVT TargetVT : MVT::fixedlen_vector_valuetypes()) {
if (TargetVT == VT)
setLoadExtAction(ISD::EXTLOAD, TargetVT, VT, Expand);
setLoadExtAction(ISD::ZEXTLOAD, TargetVT, VT, Expand);
setLoadExtAction(ISD::SEXTLOAD, TargetVT, VT, Expand);
setTruncStoreAction(VT, TargetVT, Expand);
// Normalize all inputs to SELECT to be vectors of i32.
if (VT.getVectorElementType() != MVT::i32) {
MVT VT32 = MVT::getVectorVT(MVT::i32, VT.getSizeInBits()/32);
setOperationAction(ISD::SELECT, VT, Promote);
AddPromotedToType(ISD::SELECT, VT, VT32);
setOperationAction(ISD::SRA, VT, Custom);
setOperationAction(ISD::SHL, VT, Custom);
setOperationAction(ISD::SRL, VT, Custom);
// Extending loads from (native) vectors of i8 into (native) vectors of i16
// are legal.
setLoadExtAction(ISD::EXTLOAD, MVT::v2i16, MVT::v2i8, Legal);
setLoadExtAction(ISD::ZEXTLOAD, MVT::v2i16, MVT::v2i8, Legal);
setLoadExtAction(ISD::SEXTLOAD, MVT::v2i16, MVT::v2i8, Legal);
setLoadExtAction(ISD::EXTLOAD, MVT::v4i16, MVT::v4i8, Legal);
setLoadExtAction(ISD::ZEXTLOAD, MVT::v4i16, MVT::v4i8, Legal);
setLoadExtAction(ISD::SEXTLOAD, MVT::v4i16, MVT::v4i8, Legal);
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::v2i8, Legal);
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::v2i16, Legal);
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::v2i32, Legal);
// Types natively supported:
for (MVT NativeVT : {MVT::v8i1, MVT::v4i1, MVT::v2i1, MVT::v4i8,
MVT::v8i8, MVT::v2i16, MVT::v4i16, MVT::v2i32}) {
setOperationAction(ISD::BUILD_VECTOR, NativeVT, Custom);
setOperationAction(ISD::EXTRACT_VECTOR_ELT, NativeVT, Custom);
setOperationAction(ISD::INSERT_VECTOR_ELT, NativeVT, Custom);
setOperationAction(ISD::EXTRACT_SUBVECTOR, NativeVT, Custom);
setOperationAction(ISD::INSERT_SUBVECTOR, NativeVT, Custom);
setOperationAction(ISD::CONCAT_VECTORS, NativeVT, Custom);
setOperationAction(ISD::ADD, NativeVT, Legal);
setOperationAction(ISD::SUB, NativeVT, Legal);
setOperationAction(ISD::MUL, NativeVT, Legal);
setOperationAction(ISD::AND, NativeVT, Legal);
setOperationAction(ISD::OR, NativeVT, Legal);
setOperationAction(ISD::XOR, NativeVT, Legal);
if (NativeVT.getVectorElementType() != MVT::i1) {
setOperationAction(ISD::SPLAT_VECTOR, NativeVT, Legal);
setOperationAction(ISD::BSWAP, NativeVT, Legal);
setOperationAction(ISD::BITREVERSE, NativeVT, Legal);
for (MVT VT : {MVT::v8i8, MVT::v4i16, MVT::v2i32}) {
setOperationAction(ISD::SMIN, VT, Legal);
setOperationAction(ISD::SMAX, VT, Legal);
setOperationAction(ISD::UMIN, VT, Legal);
setOperationAction(ISD::UMAX, VT, Legal);
// Custom lower unaligned loads.
// Also, for both loads and stores, verify the alignment of the address
// in case it is a compile-time constant. This is a usability feature to
// provide a meaningful error message to users.
for (MVT VT : {MVT::i16, MVT::i32, MVT::v4i8, MVT::i64, MVT::v8i8,
MVT::v2i16, MVT::v4i16, MVT::v2i32}) {
setOperationAction(ISD::LOAD, VT, Custom);
setOperationAction(ISD::STORE, VT, Custom);
// Custom-lower load/stores of boolean vectors.
for (MVT VT : {MVT::v2i1, MVT::v4i1, MVT::v8i1}) {
setOperationAction(ISD::LOAD, VT, Custom);
setOperationAction(ISD::STORE, VT, Custom);
// Normalize integer compares to EQ/GT/UGT
for (MVT VT : {MVT::v2i16, MVT::v4i8, MVT::v8i8, MVT::v2i32, MVT::v4i16,
MVT::v2i32}) {
setCondCodeAction(ISD::SETNE, VT, Expand);
setCondCodeAction(ISD::SETLE, VT, Expand);
setCondCodeAction(ISD::SETGE, VT, Expand);
setCondCodeAction(ISD::SETLT, VT, Expand);
setCondCodeAction(ISD::SETULE, VT, Expand);
setCondCodeAction(ISD::SETUGE, VT, Expand);
setCondCodeAction(ISD::SETULT, VT, Expand);
// Normalize boolean compares to [U]LE/[U]LT
for (MVT VT : {MVT::i1, MVT::v2i1, MVT::v4i1, MVT::v8i1}) {
setCondCodeAction(ISD::SETGE, VT, Expand);
setCondCodeAction(ISD::SETGT, VT, Expand);
setCondCodeAction(ISD::SETUGE, VT, Expand);
setCondCodeAction(ISD::SETUGT, VT, Expand);
// Custom-lower bitcasts from i8 to v8i1.
setOperationAction(ISD::BITCAST, MVT::i8, Custom);
setOperationAction(ISD::SETCC, MVT::v2i16, Custom);
setOperationAction(ISD::VSELECT, MVT::v4i8, Custom);
setOperationAction(ISD::VSELECT, MVT::v2i16, Custom);
setOperationAction(ISD::VECTOR_SHUFFLE, MVT::v4i8, Custom);
setOperationAction(ISD::VECTOR_SHUFFLE, MVT::v4i16, Custom);
setOperationAction(ISD::VECTOR_SHUFFLE, MVT::v8i8, Custom);
// V5+.
setOperationAction(ISD::FMA, MVT::f64, Expand);
setOperationAction(ISD::FADD, MVT::f64, Expand);
setOperationAction(ISD::FSUB, MVT::f64, Expand);
setOperationAction(ISD::FMUL, MVT::f64, Expand);
setOperationAction(ISD::FMINNUM, MVT::f32, Legal);
setOperationAction(ISD::FMAXNUM, MVT::f32, Legal);
setOperationAction(ISD::FP_TO_UINT, MVT::i1, Promote);
setOperationAction(ISD::FP_TO_UINT, MVT::i8, Promote);
setOperationAction(ISD::FP_TO_UINT, MVT::i16, Promote);
setOperationAction(ISD::FP_TO_SINT, MVT::i1, Promote);
setOperationAction(ISD::FP_TO_SINT, MVT::i8, Promote);
setOperationAction(ISD::FP_TO_SINT, MVT::i16, Promote);
setOperationAction(ISD::UINT_TO_FP, MVT::i1, Promote);
setOperationAction(ISD::UINT_TO_FP, MVT::i8, Promote);
setOperationAction(ISD::UINT_TO_FP, MVT::i16, Promote);
setOperationAction(ISD::SINT_TO_FP, MVT::i1, Promote);
setOperationAction(ISD::SINT_TO_FP, MVT::i8, Promote);
setOperationAction(ISD::SINT_TO_FP, MVT::i16, Promote);
// Special handling for half-precision floating point conversions.
// Lower half float conversions into library calls.
setOperationAction(ISD::FP16_TO_FP, MVT::f32, Expand);
setOperationAction(ISD::FP16_TO_FP, MVT::f64, Expand);
setOperationAction(ISD::FP_TO_FP16, MVT::f32, Expand);
setOperationAction(ISD::FP_TO_FP16, MVT::f64, Expand);
setLoadExtAction(ISD::EXTLOAD, MVT::f32, MVT::f16, Expand);
setLoadExtAction(ISD::EXTLOAD, MVT::f64, MVT::f16, Expand);
setTruncStoreAction(MVT::f32, MVT::f16, Expand);
setTruncStoreAction(MVT::f64, MVT::f16, Expand);
// Handling of indexed loads/stores: default is "expand".
for (MVT VT : {MVT::i8, MVT::i16, MVT::i32, MVT::i64, MVT::f32, MVT::f64,
MVT::v2i16, MVT::v2i32, MVT::v4i8, MVT::v4i16, MVT::v8i8}) {
setIndexedLoadAction(ISD::POST_INC, VT, Legal);
setIndexedStoreAction(ISD::POST_INC, VT, Legal);
// Subtarget-specific operation actions.
if (Subtarget.hasV60Ops()) {
setOperationAction(ISD::ROTL, MVT::i32, Legal);
setOperationAction(ISD::ROTL, MVT::i64, Legal);
setOperationAction(ISD::ROTR, MVT::i32, Legal);
setOperationAction(ISD::ROTR, MVT::i64, Legal);
if (Subtarget.hasV66Ops()) {
setOperationAction(ISD::FADD, MVT::f64, Legal);
setOperationAction(ISD::FSUB, MVT::f64, Legal);
if (Subtarget.hasV67Ops()) {
setOperationAction(ISD::FMINNUM, MVT::f64, Legal);
setOperationAction(ISD::FMAXNUM, MVT::f64, Legal);
setOperationAction(ISD::FMUL, MVT::f64, Legal);
if (Subtarget.useHVXOps())
// Library calls for unsupported operations
bool FastMath = EnableFastMath;
setLibcallName(RTLIB::SDIV_I32, "__hexagon_divsi3");
setLibcallName(RTLIB::SDIV_I64, "__hexagon_divdi3");
setLibcallName(RTLIB::UDIV_I32, "__hexagon_udivsi3");
setLibcallName(RTLIB::UDIV_I64, "__hexagon_udivdi3");
setLibcallName(RTLIB::SREM_I32, "__hexagon_modsi3");
setLibcallName(RTLIB::SREM_I64, "__hexagon_moddi3");
setLibcallName(RTLIB::UREM_I32, "__hexagon_umodsi3");
setLibcallName(RTLIB::UREM_I64, "__hexagon_umoddi3");
setLibcallName(RTLIB::SINTTOFP_I128_F64, "__hexagon_floattidf");
setLibcallName(RTLIB::SINTTOFP_I128_F32, "__hexagon_floattisf");
setLibcallName(RTLIB::FPTOUINT_F32_I128, "__hexagon_fixunssfti");
setLibcallName(RTLIB::FPTOUINT_F64_I128, "__hexagon_fixunsdfti");
setLibcallName(RTLIB::FPTOSINT_F32_I128, "__hexagon_fixsfti");
setLibcallName(RTLIB::FPTOSINT_F64_I128, "__hexagon_fixdfti");
// This is the only fast library function for sqrtd.
if (FastMath)
setLibcallName(RTLIB::SQRT_F64, "__hexagon_fast2_sqrtdf2");
// Prefix is: nothing for "slow-math",
// "fast2_" for V5+ fast-math double-precision
// (actually, keep fast-math and fast-math2 separate for now)
if (FastMath) {
setLibcallName(RTLIB::ADD_F64, "__hexagon_fast_adddf3");
setLibcallName(RTLIB::SUB_F64, "__hexagon_fast_subdf3");
setLibcallName(RTLIB::MUL_F64, "__hexagon_fast_muldf3");
setLibcallName(RTLIB::DIV_F64, "__hexagon_fast_divdf3");
setLibcallName(RTLIB::DIV_F32, "__hexagon_fast_divsf3");
} else {
setLibcallName(RTLIB::ADD_F64, "__hexagon_adddf3");
setLibcallName(RTLIB::SUB_F64, "__hexagon_subdf3");
setLibcallName(RTLIB::MUL_F64, "__hexagon_muldf3");
setLibcallName(RTLIB::DIV_F64, "__hexagon_divdf3");
setLibcallName(RTLIB::DIV_F32, "__hexagon_divsf3");
if (FastMath)
setLibcallName(RTLIB::SQRT_F32, "__hexagon_fast2_sqrtf");
setLibcallName(RTLIB::SQRT_F32, "__hexagon_sqrtf");
// Routines to handle fp16 storage type.
setLibcallName(RTLIB::FPROUND_F32_F16, "__truncsfhf2");
setLibcallName(RTLIB::FPROUND_F64_F16, "__truncdfhf2");
setLibcallName(RTLIB::FPEXT_F16_F32, "__extendhfsf2");
// These cause problems when the shift amount is non-constant.
setLibcallName(RTLIB::SHL_I128, nullptr);
setLibcallName(RTLIB::SRL_I128, nullptr);
setLibcallName(RTLIB::SRA_I128, nullptr);
const char* HexagonTargetLowering::getTargetNodeName(unsigned Opcode) const {
switch ((HexagonISD::NodeType)Opcode) {
case HexagonISD::ADDC: return "HexagonISD::ADDC";
case HexagonISD::SUBC: return "HexagonISD::SUBC";
case HexagonISD::ALLOCA: return "HexagonISD::ALLOCA";
case HexagonISD::AT_GOT: return "HexagonISD::AT_GOT";
case HexagonISD::AT_PCREL: return "HexagonISD::AT_PCREL";
case HexagonISD::BARRIER: return "HexagonISD::BARRIER";
case HexagonISD::CALL: return "HexagonISD::CALL";
case HexagonISD::CALLnr: return "HexagonISD::CALLnr";
case HexagonISD::CALLR: return "HexagonISD::CALLR";
case HexagonISD::COMBINE: return "HexagonISD::COMBINE";
case HexagonISD::CONST32_GP: return "HexagonISD::CONST32_GP";
case HexagonISD::CONST32: return "HexagonISD::CONST32";
case HexagonISD::CP: return "HexagonISD::CP";
case HexagonISD::DCFETCH: return "HexagonISD::DCFETCH";
case HexagonISD::EH_RETURN: return "HexagonISD::EH_RETURN";
case HexagonISD::TSTBIT: return "HexagonISD::TSTBIT";
case HexagonISD::EXTRACTU: return "HexagonISD::EXTRACTU";
case HexagonISD::INSERT: return "HexagonISD::INSERT";
case HexagonISD::JT: return "HexagonISD::JT";
case HexagonISD::RET_GLUE: return "HexagonISD::RET_GLUE";
case HexagonISD::TC_RETURN: return "HexagonISD::TC_RETURN";
case HexagonISD::VASL: return "HexagonISD::VASL";
case HexagonISD::VASR: return "HexagonISD::VASR";
case HexagonISD::VLSR: return "HexagonISD::VLSR";
case HexagonISD::MFSHL: return "HexagonISD::MFSHL";
case HexagonISD::MFSHR: return "HexagonISD::MFSHR";
case HexagonISD::SSAT: return "HexagonISD::SSAT";
case HexagonISD::USAT: return "HexagonISD::USAT";
case HexagonISD::SMUL_LOHI: return "HexagonISD::SMUL_LOHI";
case HexagonISD::UMUL_LOHI: return "HexagonISD::UMUL_LOHI";
case HexagonISD::USMUL_LOHI: return "HexagonISD::USMUL_LOHI";
case HexagonISD::VEXTRACTW: return "HexagonISD::VEXTRACTW";
case HexagonISD::VINSERTW0: return "HexagonISD::VINSERTW0";
case HexagonISD::VROR: return "HexagonISD::VROR";
case HexagonISD::READCYCLE: return "HexagonISD::READCYCLE";
case HexagonISD::PTRUE: return "HexagonISD::PTRUE";
case HexagonISD::PFALSE: return "HexagonISD::PFALSE";
case HexagonISD::D2P: return "HexagonISD::D2P";
case HexagonISD::P2D: return "HexagonISD::P2D";
case HexagonISD::V2Q: return "HexagonISD::V2Q";
case HexagonISD::Q2V: return "HexagonISD::Q2V";
case HexagonISD::QCAT: return "HexagonISD::QCAT";
case HexagonISD::QTRUE: return "HexagonISD::QTRUE";
case HexagonISD::QFALSE: return "HexagonISD::QFALSE";
case HexagonISD::TL_EXTEND: return "HexagonISD::TL_EXTEND";
case HexagonISD::TL_TRUNCATE: return "HexagonISD::TL_TRUNCATE";
case HexagonISD::TYPECAST: return "HexagonISD::TYPECAST";
case HexagonISD::VALIGN: return "HexagonISD::VALIGN";
case HexagonISD::VALIGNADDR: return "HexagonISD::VALIGNADDR";
case HexagonISD::ISEL: return "HexagonISD::ISEL";
case HexagonISD::OP_END: break;
return nullptr;
HexagonTargetLowering::validateConstPtrAlignment(SDValue Ptr, Align NeedAlign,
const SDLoc &dl, SelectionDAG &DAG) const {
auto *CA = dyn_cast<ConstantSDNode>(Ptr);
if (!CA)
return true;
unsigned Addr = CA->getZExtValue();
Align HaveAlign =
Addr != 0 ? Align(1ull << llvm::countr_zero(Addr)) : NeedAlign;
if (HaveAlign >= NeedAlign)
return true;
static int DK_MisalignedTrap = llvm::getNextAvailablePluginDiagnosticKind();
struct DiagnosticInfoMisalignedTrap : public DiagnosticInfo {
DiagnosticInfoMisalignedTrap(StringRef M)
: DiagnosticInfo(DK_MisalignedTrap, DS_Remark), Msg(M) {}
void print(DiagnosticPrinter &DP) const override {
DP << Msg;
static bool classof(const DiagnosticInfo *DI) {
return DI->getKind() == DK_MisalignedTrap;
StringRef Msg;
std::string ErrMsg;
raw_string_ostream O(ErrMsg);
O << "Misaligned constant address: " << format_hex(Addr, 10)
<< " has alignment " << HaveAlign.value()
<< ", but the memory access requires " << NeedAlign.value();
if (DebugLoc DL = dl.getDebugLoc())
DL.print(O << ", at ");
O << ". The instruction has been replaced with a trap.";
return false;
HexagonTargetLowering::replaceMemWithUndef(SDValue Op, SelectionDAG &DAG)
const {
const SDLoc &dl(Op);
auto *LS = cast<LSBaseSDNode>(Op.getNode());
assert(!LS->isIndexed() && "Not expecting indexed ops on constant address");
SDValue Chain = LS->getChain();
SDValue Trap = DAG.getNode(ISD::TRAP, dl, MVT::Other, Chain);
if (LS->getOpcode() == ISD::LOAD)
return DAG.getMergeValues({DAG.getUNDEF(ty(Op)), Trap}, dl);
return Trap;
// Bit-reverse Load Intrinsic: Check if the instruction is a bit reverse load
// intrinsic.
static bool isBrevLdIntrinsic(const Value *Inst) {
unsigned ID = cast<IntrinsicInst>(Inst)->getIntrinsicID();
return (ID == Intrinsic::hexagon_L2_loadrd_pbr ||
ID == Intrinsic::hexagon_L2_loadri_pbr ||
ID == Intrinsic::hexagon_L2_loadrh_pbr ||
ID == Intrinsic::hexagon_L2_loadruh_pbr ||
ID == Intrinsic::hexagon_L2_loadrb_pbr ||
ID == Intrinsic::hexagon_L2_loadrub_pbr);
// Bit-reverse Load Intrinsic :Crawl up and figure out the object from previous
// instruction. So far we only handle bitcast, extract value and bit reverse
// load intrinsic instructions. Should we handle CGEP ?
static Value *getBrevLdObject(Value *V) {
if (Operator::getOpcode(V) == Instruction::ExtractValue ||
Operator::getOpcode(V) == Instruction::BitCast)
V = cast<Operator>(V)->getOperand(0);
else if (isa<IntrinsicInst>(V) && isBrevLdIntrinsic(V))
V = cast<Instruction>(V)->getOperand(0);
return V;
// Bit-reverse Load Intrinsic: For a PHI Node return either an incoming edge or
// a back edge. If the back edge comes from the intrinsic itself, the incoming
// edge is returned.
static Value *returnEdge(const PHINode *PN, Value *IntrBaseVal) {
const BasicBlock *Parent = PN->getParent();
int Idx = -1;
for (unsigned i = 0, e = PN->getNumIncomingValues(); i < e; ++i) {
BasicBlock *Blk = PN->getIncomingBlock(i);
// Determine if the back edge is originated from intrinsic.
if (Blk == Parent) {
Value *BackEdgeVal = PN->getIncomingValue(i);
Value *BaseVal;
// Loop over till we return the same Value or we hit the IntrBaseVal.
do {
BaseVal = BackEdgeVal;
BackEdgeVal = getBrevLdObject(BackEdgeVal);
} while ((BaseVal != BackEdgeVal) && (IntrBaseVal != BackEdgeVal));
// If the getBrevLdObject returns IntrBaseVal, we should return the
// incoming edge.
if (IntrBaseVal == BackEdgeVal)
Idx = i;
} else // Set the node to incoming edge.
Idx = i;
assert(Idx >= 0 && "Unexpected index to incoming argument in PHI");
return PN->getIncomingValue(Idx);
// Bit-reverse Load Intrinsic: Figure out the underlying object the base
// pointer points to, for the bit-reverse load intrinsic. Setting this to
// memoperand might help alias analysis to figure out the dependencies.
static Value *getUnderLyingObjectForBrevLdIntr(Value *V) {
Value *IntrBaseVal = V;
Value *BaseVal;
// Loop over till we return the same Value, implies we either figure out
// the object or we hit a PHI
do {
BaseVal = V;
V = getBrevLdObject(V);
} while (BaseVal != V);
// Identify the object from PHINode.
if (const PHINode *PN = dyn_cast<PHINode>(V))
return returnEdge(PN, IntrBaseVal);
// For non PHI nodes, the object is the last value returned by getBrevLdObject
return V;
/// Given an intrinsic, checks if on the target the intrinsic will need to map
/// to a MemIntrinsicNode (touches memory). If this is the case, it returns
/// true and store the intrinsic information into the IntrinsicInfo that was
/// passed to the function.
bool HexagonTargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info,
const CallInst &I,
MachineFunction &MF,
unsigned Intrinsic) const {
switch (Intrinsic) {
case Intrinsic::hexagon_L2_loadrd_pbr:
case Intrinsic::hexagon_L2_loadri_pbr:
case Intrinsic::hexagon_L2_loadrh_pbr:
case Intrinsic::hexagon_L2_loadruh_pbr:
case Intrinsic::hexagon_L2_loadrb_pbr:
case Intrinsic::hexagon_L2_loadrub_pbr: {
auto &DL = I.getCalledFunction()->getParent()->getDataLayout();
auto &Cont = I.getCalledFunction()->getParent()->getContext();
// The intrinsic function call is of the form { ElTy, i8* }
// @llvm.hexagon.L2.loadXX.pbr(i8*, i32). The pointer and memory access type
// should be derived from ElTy.
Type *ElTy = I.getCalledFunction()->getReturnType()->getStructElementType(0);
Info.memVT = MVT::getVT(ElTy);
llvm::Value *BasePtrVal = I.getOperand(0);
Info.ptrVal = getUnderLyingObjectForBrevLdIntr(BasePtrVal);
// The offset value comes through Modifier register. For now, assume the
// offset is 0.
Info.offset = 0;
Info.align = DL.getABITypeAlign(Info.memVT.getTypeForEVT(Cont));
Info.flags = MachineMemOperand::MOLoad;
return true;
case Intrinsic::hexagon_V6_vgathermw:
case Intrinsic::hexagon_V6_vgathermw_128B:
case Intrinsic::hexagon_V6_vgathermh:
case Intrinsic::hexagon_V6_vgathermh_128B:
case Intrinsic::hexagon_V6_vgathermhw:
case Intrinsic::hexagon_V6_vgathermhw_128B:
case Intrinsic::hexagon_V6_vgathermwq:
case Intrinsic::hexagon_V6_vgathermwq_128B:
case Intrinsic::hexagon_V6_vgathermhq:
case Intrinsic::hexagon_V6_vgathermhq_128B:
case Intrinsic::hexagon_V6_vgathermhwq:
case Intrinsic::hexagon_V6_vgathermhwq_128B: {
const Module &M = *I.getParent()->getParent()->getParent();
Type *VecTy = I.getArgOperand(1)->getType();
Info.memVT = MVT::getVT(VecTy);
Info.ptrVal = I.getArgOperand(0);
Info.offset = 0;
Info.align =
MaybeAlign(M.getDataLayout().getTypeAllocSizeInBits(VecTy) / 8);
Info.flags = MachineMemOperand::MOLoad |
MachineMemOperand::MOStore |
return true;
return false;
bool HexagonTargetLowering::hasBitTest(SDValue X, SDValue Y) const {
return X.getValueType().isScalarInteger(); // 'tstbit'
bool HexagonTargetLowering::isTruncateFree(Type *Ty1, Type *Ty2) const {
return isTruncateFree(EVT::getEVT(Ty1), EVT::getEVT(Ty2));
bool HexagonTargetLowering::isTruncateFree(EVT VT1, EVT VT2) const {
if (!VT1.isSimple() || !VT2.isSimple())
return false;
return VT1.getSimpleVT() == MVT::i64 && VT2.getSimpleVT() == MVT::i32;
bool HexagonTargetLowering::isFMAFasterThanFMulAndFAdd(
const MachineFunction &MF, EVT VT) const {
return isOperationLegalOrCustom(ISD::FMA, VT);
// Should we expand the build vector with shuffles?
bool HexagonTargetLowering::shouldExpandBuildVectorWithShuffles(EVT VT,
unsigned DefinedValues) const {
return false;
bool HexagonTargetLowering::isExtractSubvectorCheap(EVT ResVT, EVT SrcVT,
unsigned Index) const {
assert(ResVT.getVectorElementType() == SrcVT.getVectorElementType());
if (!ResVT.isSimple() || !SrcVT.isSimple())
return false;
MVT ResTy = ResVT.getSimpleVT(), SrcTy = SrcVT.getSimpleVT();
if (ResTy.getVectorElementType() != MVT::i1)
return true;
// Non-HVX bool vectors are relatively cheap.
return SrcTy.getVectorNumElements() <= 8;
bool HexagonTargetLowering::isTargetCanonicalConstantNode(SDValue Op) const {
return Op.getOpcode() == ISD::CONCAT_VECTORS ||
bool HexagonTargetLowering::isShuffleMaskLegal(ArrayRef<int> Mask,
EVT VT) const {
return true;
HexagonTargetLowering::getPreferredVectorAction(MVT VT) const {
unsigned VecLen = VT.getVectorMinNumElements();
MVT ElemTy = VT.getVectorElementType();
if (VecLen == 1 || VT.isScalableVector())
return TargetLoweringBase::TypeScalarizeVector;
if (Subtarget.useHVXOps()) {
unsigned Action = getPreferredHvxVectorAction(VT);
if (Action != ~0u)
return static_cast<TargetLoweringBase::LegalizeTypeAction>(Action);
// Always widen (remaining) vectors of i1.
if (ElemTy == MVT::i1)
return TargetLoweringBase::TypeWidenVector;
// Widen non-power-of-2 vectors. Such types cannot be split right now,
// and computeRegisterProperties will override "split" with "widen",
// which can cause other issues.
if (!isPowerOf2_32(VecLen))
return TargetLoweringBase::TypeWidenVector;
return TargetLoweringBase::TypeSplitVector;
HexagonTargetLowering::getCustomOperationAction(SDNode &Op) const {
if (Subtarget.useHVXOps()) {
unsigned Action = getCustomHvxOperationAction(Op);
if (Action != ~0u)
return static_cast<TargetLoweringBase::LegalizeAction>(Action);
return TargetLoweringBase::Legal;
std::pair<SDValue, int>
HexagonTargetLowering::getBaseAndOffset(SDValue Addr) const {
if (Addr.getOpcode() == ISD::ADD) {
SDValue Op1 = Addr.getOperand(1);
if (auto *CN = dyn_cast<const ConstantSDNode>(Op1.getNode()))
return { Addr.getOperand(0), CN->getSExtValue() };
return { Addr, 0 };
// Lower a vector shuffle (V1, V2, V3). V1 and V2 are the two vectors
// to select data from, V3 is the permutation.
HexagonTargetLowering::LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG)
const {
const auto *SVN = cast<ShuffleVectorSDNode>(Op);
ArrayRef<int> AM = SVN->getMask();
assert(AM.size() <= 8 && "Unexpected shuffle mask");
unsigned VecLen = AM.size();
MVT VecTy = ty(Op);
assert(!Subtarget.isHVXVectorType(VecTy, true) &&
"HVX shuffles should be legal");
assert(VecTy.getSizeInBits() <= 64 && "Unexpected vector length");
SDValue Op0 = Op.getOperand(0);
SDValue Op1 = Op.getOperand(1);
const SDLoc &dl(Op);
// If the inputs are not the same as the output, bail. This is not an
// error situation, but complicates the handling and the default expansion
// (into BUILD_VECTOR) should be adequate.
if (ty(Op0) != VecTy || ty(Op1) != VecTy)
return SDValue();
// Normalize the mask so that the first non-negative index comes from
// the first operand.
SmallVector<int,8> Mask(AM.begin(), AM.end());
unsigned F = llvm::find_if(AM, [](int M) { return M >= 0; }) -;
if (F == AM.size())
return DAG.getUNDEF(VecTy);
if (AM[F] >= int(VecLen)) {
std::swap(Op0, Op1);
// Express the shuffle mask in terms of bytes.
SmallVector<int,8> ByteMask;
unsigned ElemBytes = VecTy.getVectorElementType().getSizeInBits() / 8;
for (int M : Mask) {
if (M < 0) {
for (unsigned j = 0; j != ElemBytes; ++j)
} else {
for (unsigned j = 0; j != ElemBytes; ++j)
ByteMask.push_back(M*ElemBytes + j);
assert(ByteMask.size() <= 8);
// All non-undef (non-negative) indexes are well within [0..127], so they
// fit in a single byte. Build two 64-bit words:
// - MaskIdx where each byte is the corresponding index (for non-negative
// indexes), and 0xFF for negative indexes, and
// - MaskUnd that has 0xFF for each negative index.
uint64_t MaskIdx = 0;
uint64_t MaskUnd = 0;
for (unsigned i = 0, e = ByteMask.size(); i != e; ++i) {
unsigned S = 8*i;
uint64_t M = ByteMask[i] & 0xFF;
if (M == 0xFF)
MaskUnd |= M << S;
MaskIdx |= M << S;
if (ByteMask.size() == 4) {
// Identity.
if (MaskIdx == (0x03020100 | MaskUnd))
return Op0;
// Byte swap.
if (MaskIdx == (0x00010203 | MaskUnd)) {
SDValue T0 = DAG.getBitcast(MVT::i32, Op0);
SDValue T1 = DAG.getNode(ISD::BSWAP, dl, MVT::i32, T0);
return DAG.getBitcast(VecTy, T1);
// Byte packs.
SDValue Concat10 =
getCombine(Op1, Op0, dl, typeJoin({ty(Op1), ty(Op0)}), DAG);
if (MaskIdx == (0x06040200 | MaskUnd))
return getInstr(Hexagon::S2_vtrunehb, dl, VecTy, {Concat10}, DAG);
if (MaskIdx == (0x07050301 | MaskUnd))
return getInstr(Hexagon::S2_vtrunohb, dl, VecTy, {Concat10}, DAG);
SDValue Concat01 =
getCombine(Op0, Op1, dl, typeJoin({ty(Op0), ty(Op1)}), DAG);
if (MaskIdx == (0x02000604 | MaskUnd))
return getInstr(Hexagon::S2_vtrunehb, dl, VecTy, {Concat01}, DAG);
if (MaskIdx == (0x03010705 | MaskUnd))
return getInstr(Hexagon::S2_vtrunohb, dl, VecTy, {Concat01}, DAG);
if (ByteMask.size() == 8) {
// Identity.
if (MaskIdx == (0x0706050403020100ull | MaskUnd))
return Op0;
// Byte swap.
if (MaskIdx == (0x0001020304050607ull | MaskUnd)) {
SDValue T0 = DAG.getBitcast(MVT::i64, Op0);
SDValue T1 = DAG.getNode(ISD::BSWAP, dl, MVT::i64, T0);
return DAG.getBitcast(VecTy, T1);
// Halfword picks.
if (MaskIdx == (0x0d0c050409080100ull | MaskUnd))
return getInstr(Hexagon::S2_shuffeh, dl, VecTy, {Op1, Op0}, DAG);
if (MaskIdx == (0x0f0e07060b0a0302ull | MaskUnd))
return getInstr(Hexagon::S2_shuffoh, dl, VecTy, {Op1, Op0}, DAG);
if (MaskIdx == (0x0d0c090805040100ull | MaskUnd))
return getInstr(Hexagon::S2_vtrunewh, dl, VecTy, {Op1, Op0}, DAG);
if (MaskIdx == (0x0f0e0b0a07060302ull | MaskUnd))
return getInstr(Hexagon::S2_vtrunowh, dl, VecTy, {Op1, Op0}, DAG);
if (MaskIdx == (0x0706030205040100ull | MaskUnd)) {
VectorPair P = opSplit(Op0, dl, DAG);
return getInstr(Hexagon::S2_packhl, dl, VecTy, {P.second, P.first}, DAG);
// Byte packs.
if (MaskIdx == (0x0e060c040a020800ull | MaskUnd))
return getInstr(Hexagon::S2_shuffeb, dl, VecTy, {Op1, Op0}, DAG);
if (MaskIdx == (0x0f070d050b030901ull | MaskUnd))
return getInstr(Hexagon::S2_shuffob, dl, VecTy, {Op1, Op0}, DAG);
return SDValue();
HexagonTargetLowering::getSplatValue(SDValue Op, SelectionDAG &DAG) const {
switch (Op.getOpcode()) {
if (SDValue S = cast<BuildVectorSDNode>(Op)->getSplatValue())
return S;
return Op.getOperand(0);
return SDValue();
// Create a Hexagon-specific node for shifting a vector by an integer.
HexagonTargetLowering::getVectorShiftByInt(SDValue Op, SelectionDAG &DAG)
const {
unsigned NewOpc;
switch (Op.getOpcode()) {
case ISD::SHL:
NewOpc = HexagonISD::VASL;
case ISD::SRA:
NewOpc = HexagonISD::VASR;
case ISD::SRL:
NewOpc = HexagonISD::VLSR;
llvm_unreachable("Unexpected shift opcode");
if (SDValue Sp = getSplatValue(Op.getOperand(1), DAG))
return DAG.getNode(NewOpc, SDLoc(Op), ty(Op), Op.getOperand(0), Sp);
return SDValue();
HexagonTargetLowering::LowerVECTOR_SHIFT(SDValue Op, SelectionDAG &DAG) const {
const SDLoc &dl(Op);
// First try to convert the shift (by vector) to a shift by a scalar.
// If we first split the shift, the shift amount will become 'extract
// subvector', and will no longer be recognized as scalar.
SDValue Res = Op;
if (SDValue S = getVectorShiftByInt(Op, DAG))
Res = S;
unsigned Opc = Res.getOpcode();
switch (Opc) {
case HexagonISD::VASR:
case HexagonISD::VLSR:
case HexagonISD::VASL:
// No instructions for shifts by non-scalars.
return SDValue();
MVT ResTy = ty(Res);
if (ResTy.getVectorElementType() != MVT::i8)
return Res;
// For shifts of i8, extend the inputs to i16, then truncate back to i8.
assert(ResTy.getVectorElementType() == MVT::i8);
SDValue Val = Res.getOperand(0), Amt = Res.getOperand(1);
auto ShiftPartI8 = [&dl, &DAG, this](unsigned Opc, SDValue V, SDValue A) {
MVT Ty = ty(V);
MVT ExtTy = MVT::getVectorVT(MVT::i16, Ty.getVectorNumElements());
SDValue ExtV = Opc == HexagonISD::VASR ? DAG.getSExtOrTrunc(V, dl, ExtTy)
: DAG.getZExtOrTrunc(V, dl, ExtTy);
SDValue ExtS = DAG.getNode(Opc, dl, ExtTy, {ExtV, A});
return DAG.getZExtOrTrunc(ExtS, dl, Ty);
if (ResTy.getSizeInBits() == 32)
return ShiftPartI8(Opc, Val, Amt);
auto [LoV, HiV] = opSplit(Val, dl, DAG);
return DAG.getNode(ISD::CONCAT_VECTORS, dl, ResTy,
{ShiftPartI8(Opc, LoV, Amt), ShiftPartI8(Opc, HiV, Amt)});
HexagonTargetLowering::LowerROTL(SDValue Op, SelectionDAG &DAG) const {
if (isa<ConstantSDNode>(Op.getOperand(1).getNode()))
return Op;
return SDValue();
HexagonTargetLowering::LowerBITCAST(SDValue Op, SelectionDAG &DAG) const {
MVT ResTy = ty(Op);
SDValue InpV = Op.getOperand(0);
MVT InpTy = ty(InpV);
assert(ResTy.getSizeInBits() == InpTy.getSizeInBits());
const SDLoc &dl(Op);
// Handle conversion from i8 to v8i1.
if (InpTy == MVT::i8) {
if (ResTy == MVT::v8i1) {
SDValue Sc = DAG.getBitcast(tyScalar(InpTy), InpV);
SDValue Ext = DAG.getZExtOrTrunc(Sc, dl, MVT::i32);
return getInstr(Hexagon::C2_tfrrp, dl, ResTy, Ext, DAG);