418 lines
14 KiB
C++
418 lines
14 KiB
C++
//=- LoongArchISelDAGToDAG.cpp - A dag to dag inst selector for LoongArch -===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines an instruction selector for the LoongArch target.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "LoongArchISelDAGToDAG.h"
|
|
#include "LoongArchISelLowering.h"
|
|
#include "MCTargetDesc/LoongArchMCTargetDesc.h"
|
|
#include "MCTargetDesc/LoongArchMatInt.h"
|
|
#include "llvm/Support/KnownBits.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "loongarch-isel"
|
|
#define PASS_NAME "LoongArch DAG->DAG Pattern Instruction Selection"
|
|
|
|
char LoongArchDAGToDAGISel::ID;
|
|
|
|
INITIALIZE_PASS(LoongArchDAGToDAGISel, DEBUG_TYPE, PASS_NAME, false, false)
|
|
|
|
void LoongArchDAGToDAGISel::Select(SDNode *Node) {
|
|
// If we have a custom node, we have already selected.
|
|
if (Node->isMachineOpcode()) {
|
|
LLVM_DEBUG(dbgs() << "== "; Node->dump(CurDAG); dbgs() << "\n");
|
|
Node->setNodeId(-1);
|
|
return;
|
|
}
|
|
|
|
// Instruction Selection not handled by the auto-generated tablegen selection
|
|
// should be handled here.
|
|
unsigned Opcode = Node->getOpcode();
|
|
MVT GRLenVT = Subtarget->getGRLenVT();
|
|
SDLoc DL(Node);
|
|
MVT VT = Node->getSimpleValueType(0);
|
|
|
|
switch (Opcode) {
|
|
default:
|
|
break;
|
|
case ISD::Constant: {
|
|
int64_t Imm = cast<ConstantSDNode>(Node)->getSExtValue();
|
|
if (Imm == 0 && VT == GRLenVT) {
|
|
SDValue New = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), DL,
|
|
LoongArch::R0, GRLenVT);
|
|
ReplaceNode(Node, New.getNode());
|
|
return;
|
|
}
|
|
SDNode *Result = nullptr;
|
|
SDValue SrcReg = CurDAG->getRegister(LoongArch::R0, GRLenVT);
|
|
// The instructions in the sequence are handled here.
|
|
for (LoongArchMatInt::Inst &Inst : LoongArchMatInt::generateInstSeq(Imm)) {
|
|
SDValue SDImm = CurDAG->getTargetConstant(Inst.Imm, DL, GRLenVT);
|
|
if (Inst.Opc == LoongArch::LU12I_W)
|
|
Result = CurDAG->getMachineNode(LoongArch::LU12I_W, DL, GRLenVT, SDImm);
|
|
else
|
|
Result = CurDAG->getMachineNode(Inst.Opc, DL, GRLenVT, SrcReg, SDImm);
|
|
SrcReg = SDValue(Result, 0);
|
|
}
|
|
|
|
ReplaceNode(Node, Result);
|
|
return;
|
|
}
|
|
case ISD::FrameIndex: {
|
|
SDValue Imm = CurDAG->getTargetConstant(0, DL, GRLenVT);
|
|
int FI = cast<FrameIndexSDNode>(Node)->getIndex();
|
|
SDValue TFI = CurDAG->getTargetFrameIndex(FI, VT);
|
|
unsigned ADDIOp =
|
|
Subtarget->is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
|
|
ReplaceNode(Node, CurDAG->getMachineNode(ADDIOp, DL, VT, TFI, Imm));
|
|
return;
|
|
}
|
|
case ISD::BITCAST: {
|
|
if (VT.is128BitVector() || VT.is256BitVector()) {
|
|
ReplaceUses(SDValue(Node, 0), Node->getOperand(0));
|
|
CurDAG->RemoveDeadNode(Node);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case ISD::BUILD_VECTOR: {
|
|
// Select appropriate [x]vrepli.[bhwd] instructions for constant splats of
|
|
// 128/256-bit when LSX/LASX is enabled.
|
|
BuildVectorSDNode *BVN = cast<BuildVectorSDNode>(Node);
|
|
APInt SplatValue, SplatUndef;
|
|
unsigned SplatBitSize;
|
|
bool HasAnyUndefs;
|
|
unsigned Op;
|
|
EVT ViaVecTy;
|
|
bool Is128Vec = BVN->getValueType(0).is128BitVector();
|
|
bool Is256Vec = BVN->getValueType(0).is256BitVector();
|
|
|
|
if (!Subtarget->hasExtLSX() || (!Is128Vec && !Is256Vec))
|
|
break;
|
|
if (!BVN->isConstantSplat(SplatValue, SplatUndef, SplatBitSize,
|
|
HasAnyUndefs, 8))
|
|
break;
|
|
|
|
switch (SplatBitSize) {
|
|
default:
|
|
break;
|
|
case 8:
|
|
Op = Is256Vec ? LoongArch::PseudoXVREPLI_B : LoongArch::PseudoVREPLI_B;
|
|
ViaVecTy = Is256Vec ? MVT::v32i8 : MVT::v16i8;
|
|
break;
|
|
case 16:
|
|
Op = Is256Vec ? LoongArch::PseudoXVREPLI_H : LoongArch::PseudoVREPLI_H;
|
|
ViaVecTy = Is256Vec ? MVT::v16i16 : MVT::v8i16;
|
|
break;
|
|
case 32:
|
|
Op = Is256Vec ? LoongArch::PseudoXVREPLI_W : LoongArch::PseudoVREPLI_W;
|
|
ViaVecTy = Is256Vec ? MVT::v8i32 : MVT::v4i32;
|
|
break;
|
|
case 64:
|
|
Op = Is256Vec ? LoongArch::PseudoXVREPLI_D : LoongArch::PseudoVREPLI_D;
|
|
ViaVecTy = Is256Vec ? MVT::v4i64 : MVT::v2i64;
|
|
break;
|
|
}
|
|
|
|
SDNode *Res;
|
|
// If we have a signed 10 bit integer, we can splat it directly.
|
|
if (SplatValue.isSignedIntN(10)) {
|
|
SDValue Imm = CurDAG->getTargetConstant(SplatValue, DL,
|
|
ViaVecTy.getVectorElementType());
|
|
Res = CurDAG->getMachineNode(Op, DL, ViaVecTy, Imm);
|
|
ReplaceNode(Node, Res);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Select the default instruction.
|
|
SelectCode(Node);
|
|
}
|
|
|
|
bool LoongArchDAGToDAGISel::SelectInlineAsmMemoryOperand(
|
|
const SDValue &Op, InlineAsm::ConstraintCode ConstraintID,
|
|
std::vector<SDValue> &OutOps) {
|
|
SDValue Base = Op;
|
|
SDValue Offset =
|
|
CurDAG->getTargetConstant(0, SDLoc(Op), Subtarget->getGRLenVT());
|
|
switch (ConstraintID) {
|
|
default:
|
|
llvm_unreachable("unexpected asm memory constraint");
|
|
// Reg+Reg addressing.
|
|
case InlineAsm::ConstraintCode::k:
|
|
Base = Op.getOperand(0);
|
|
Offset = Op.getOperand(1);
|
|
break;
|
|
// Reg+simm12 addressing.
|
|
case InlineAsm::ConstraintCode::m:
|
|
if (CurDAG->isBaseWithConstantOffset(Op)) {
|
|
ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Op.getOperand(1));
|
|
if (isIntN(12, CN->getSExtValue())) {
|
|
Base = Op.getOperand(0);
|
|
Offset = CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Op),
|
|
Op.getValueType());
|
|
}
|
|
}
|
|
break;
|
|
// Reg+0 addressing.
|
|
case InlineAsm::ConstraintCode::ZB:
|
|
break;
|
|
// Reg+(simm14<<2) addressing.
|
|
case InlineAsm::ConstraintCode::ZC:
|
|
if (CurDAG->isBaseWithConstantOffset(Op)) {
|
|
ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Op.getOperand(1));
|
|
if (isIntN(16, CN->getSExtValue()) &&
|
|
isAligned(Align(4ULL), CN->getZExtValue())) {
|
|
Base = Op.getOperand(0);
|
|
Offset = CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Op),
|
|
Op.getValueType());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
OutOps.push_back(Base);
|
|
OutOps.push_back(Offset);
|
|
return false;
|
|
}
|
|
|
|
bool LoongArchDAGToDAGISel::SelectBaseAddr(SDValue Addr, SDValue &Base) {
|
|
// If this is FrameIndex, select it directly. Otherwise just let it get
|
|
// selected to a register independently.
|
|
if (auto *FIN = dyn_cast<FrameIndexSDNode>(Addr))
|
|
Base =
|
|
CurDAG->getTargetFrameIndex(FIN->getIndex(), Subtarget->getGRLenVT());
|
|
else
|
|
Base = Addr;
|
|
return true;
|
|
}
|
|
|
|
// Fold constant addresses.
|
|
bool LoongArchDAGToDAGISel::SelectAddrConstant(SDValue Addr, SDValue &Base,
|
|
SDValue &Offset) {
|
|
SDLoc DL(Addr);
|
|
MVT VT = Addr.getSimpleValueType();
|
|
|
|
if (!isa<ConstantSDNode>(Addr))
|
|
return false;
|
|
|
|
// If the constant is a simm12, we can fold the whole constant and use R0 as
|
|
// the base.
|
|
int64_t CVal = cast<ConstantSDNode>(Addr)->getSExtValue();
|
|
if (!isInt<12>(CVal))
|
|
return false;
|
|
Base = CurDAG->getRegister(LoongArch::R0, VT);
|
|
Offset = CurDAG->getTargetConstant(SignExtend64<12>(CVal), DL, VT);
|
|
return true;
|
|
}
|
|
|
|
bool LoongArchDAGToDAGISel::selectNonFIBaseAddr(SDValue Addr, SDValue &Base) {
|
|
// If this is FrameIndex, don't select it.
|
|
if (isa<FrameIndexSDNode>(Addr))
|
|
return false;
|
|
Base = Addr;
|
|
return true;
|
|
}
|
|
|
|
bool LoongArchDAGToDAGISel::selectShiftMask(SDValue N, unsigned ShiftWidth,
|
|
SDValue &ShAmt) {
|
|
// Shift instructions on LoongArch only read the lower 5 or 6 bits of the
|
|
// shift amount. If there is an AND on the shift amount, we can bypass it if
|
|
// it doesn't affect any of those bits.
|
|
if (N.getOpcode() == ISD::AND && isa<ConstantSDNode>(N.getOperand(1))) {
|
|
const APInt &AndMask = N->getConstantOperandAPInt(1);
|
|
|
|
// Since the max shift amount is a power of 2 we can subtract 1 to make a
|
|
// mask that covers the bits needed to represent all shift amounts.
|
|
assert(isPowerOf2_32(ShiftWidth) && "Unexpected max shift amount!");
|
|
APInt ShMask(AndMask.getBitWidth(), ShiftWidth - 1);
|
|
|
|
if (ShMask.isSubsetOf(AndMask)) {
|
|
ShAmt = N.getOperand(0);
|
|
return true;
|
|
}
|
|
|
|
// SimplifyDemandedBits may have optimized the mask so try restoring any
|
|
// bits that are known zero.
|
|
KnownBits Known = CurDAG->computeKnownBits(N->getOperand(0));
|
|
if (ShMask.isSubsetOf(AndMask | Known.Zero)) {
|
|
ShAmt = N.getOperand(0);
|
|
return true;
|
|
}
|
|
} else if (N.getOpcode() == LoongArchISD::BSTRPICK) {
|
|
// Similar to the above AND, if there is a BSTRPICK on the shift amount, we
|
|
// can bypass it.
|
|
assert(isPowerOf2_32(ShiftWidth) && "Unexpected max shift amount!");
|
|
assert(isa<ConstantSDNode>(N.getOperand(1)) && "Illegal msb operand!");
|
|
assert(isa<ConstantSDNode>(N.getOperand(2)) && "Illegal lsb operand!");
|
|
uint64_t msb = N.getConstantOperandVal(1), lsb = N.getConstantOperandVal(2);
|
|
if (lsb == 0 && Log2_32(ShiftWidth) <= msb + 1) {
|
|
ShAmt = N.getOperand(0);
|
|
return true;
|
|
}
|
|
} else if (N.getOpcode() == ISD::SUB &&
|
|
isa<ConstantSDNode>(N.getOperand(0))) {
|
|
uint64_t Imm = N.getConstantOperandVal(0);
|
|
// If we are shifting by N-X where N == 0 mod Size, then just shift by -X to
|
|
// generate a NEG instead of a SUB of a constant.
|
|
if (Imm != 0 && Imm % ShiftWidth == 0) {
|
|
SDLoc DL(N);
|
|
EVT VT = N.getValueType();
|
|
SDValue Zero =
|
|
CurDAG->getCopyFromReg(CurDAG->getEntryNode(), DL, LoongArch::R0, VT);
|
|
unsigned NegOpc = VT == MVT::i64 ? LoongArch::SUB_D : LoongArch::SUB_W;
|
|
MachineSDNode *Neg =
|
|
CurDAG->getMachineNode(NegOpc, DL, VT, Zero, N.getOperand(1));
|
|
ShAmt = SDValue(Neg, 0);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
ShAmt = N;
|
|
return true;
|
|
}
|
|
|
|
bool LoongArchDAGToDAGISel::selectSExti32(SDValue N, SDValue &Val) {
|
|
if (N.getOpcode() == ISD::SIGN_EXTEND_INREG &&
|
|
cast<VTSDNode>(N.getOperand(1))->getVT() == MVT::i32) {
|
|
Val = N.getOperand(0);
|
|
return true;
|
|
}
|
|
if (N.getOpcode() == LoongArchISD::BSTRPICK &&
|
|
N.getConstantOperandVal(1) < UINT64_C(0X1F) &&
|
|
N.getConstantOperandVal(2) == UINT64_C(0)) {
|
|
Val = N;
|
|
return true;
|
|
}
|
|
MVT VT = N.getSimpleValueType();
|
|
if (CurDAG->ComputeNumSignBits(N) > (VT.getSizeInBits() - 32)) {
|
|
Val = N;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool LoongArchDAGToDAGISel::selectZExti32(SDValue N, SDValue &Val) {
|
|
if (N.getOpcode() == ISD::AND) {
|
|
auto *C = dyn_cast<ConstantSDNode>(N.getOperand(1));
|
|
if (C && C->getZExtValue() == UINT64_C(0xFFFFFFFF)) {
|
|
Val = N.getOperand(0);
|
|
return true;
|
|
}
|
|
}
|
|
MVT VT = N.getSimpleValueType();
|
|
APInt Mask = APInt::getHighBitsSet(VT.getSizeInBits(), 32);
|
|
if (CurDAG->MaskedValueIsZero(N, Mask)) {
|
|
Val = N;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool LoongArchDAGToDAGISel::selectVSplat(SDNode *N, APInt &Imm,
|
|
unsigned MinSizeInBits) const {
|
|
if (!Subtarget->hasExtLSX())
|
|
return false;
|
|
|
|
BuildVectorSDNode *Node = dyn_cast<BuildVectorSDNode>(N);
|
|
|
|
if (!Node)
|
|
return false;
|
|
|
|
APInt SplatValue, SplatUndef;
|
|
unsigned SplatBitSize;
|
|
bool HasAnyUndefs;
|
|
|
|
if (!Node->isConstantSplat(SplatValue, SplatUndef, SplatBitSize, HasAnyUndefs,
|
|
MinSizeInBits, /*IsBigEndian=*/false))
|
|
return false;
|
|
|
|
Imm = SplatValue;
|
|
|
|
return true;
|
|
}
|
|
|
|
template <unsigned ImmBitSize, bool IsSigned>
|
|
bool LoongArchDAGToDAGISel::selectVSplatImm(SDValue N, SDValue &SplatVal) {
|
|
APInt ImmValue;
|
|
EVT EltTy = N->getValueType(0).getVectorElementType();
|
|
|
|
if (N->getOpcode() == ISD::BITCAST)
|
|
N = N->getOperand(0);
|
|
|
|
if (selectVSplat(N.getNode(), ImmValue, EltTy.getSizeInBits()) &&
|
|
ImmValue.getBitWidth() == EltTy.getSizeInBits()) {
|
|
if (IsSigned && ImmValue.isSignedIntN(ImmBitSize)) {
|
|
SplatVal = CurDAG->getTargetConstant(ImmValue.getSExtValue(), SDLoc(N),
|
|
Subtarget->getGRLenVT());
|
|
return true;
|
|
}
|
|
if (!IsSigned && ImmValue.isIntN(ImmBitSize)) {
|
|
SplatVal = CurDAG->getTargetConstant(ImmValue.getZExtValue(), SDLoc(N),
|
|
Subtarget->getGRLenVT());
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool LoongArchDAGToDAGISel::selectVSplatUimmInvPow2(SDValue N,
|
|
SDValue &SplatImm) const {
|
|
APInt ImmValue;
|
|
EVT EltTy = N->getValueType(0).getVectorElementType();
|
|
|
|
if (N->getOpcode() == ISD::BITCAST)
|
|
N = N->getOperand(0);
|
|
|
|
if (selectVSplat(N.getNode(), ImmValue, EltTy.getSizeInBits()) &&
|
|
ImmValue.getBitWidth() == EltTy.getSizeInBits()) {
|
|
int32_t Log2 = (~ImmValue).exactLogBase2();
|
|
|
|
if (Log2 != -1) {
|
|
SplatImm = CurDAG->getTargetConstant(Log2, SDLoc(N), EltTy);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool LoongArchDAGToDAGISel::selectVSplatUimmPow2(SDValue N,
|
|
SDValue &SplatImm) const {
|
|
APInt ImmValue;
|
|
EVT EltTy = N->getValueType(0).getVectorElementType();
|
|
|
|
if (N->getOpcode() == ISD::BITCAST)
|
|
N = N->getOperand(0);
|
|
|
|
if (selectVSplat(N.getNode(), ImmValue, EltTy.getSizeInBits()) &&
|
|
ImmValue.getBitWidth() == EltTy.getSizeInBits()) {
|
|
int32_t Log2 = ImmValue.exactLogBase2();
|
|
|
|
if (Log2 != -1) {
|
|
SplatImm = CurDAG->getTargetConstant(Log2, SDLoc(N), EltTy);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// This pass converts a legalized DAG into a LoongArch-specific DAG, ready
|
|
// for instruction scheduling.
|
|
FunctionPass *llvm::createLoongArchISelDag(LoongArchTargetMachine &TM) {
|
|
return new LoongArchDAGToDAGISel(TM);
|
|
}
|