blob: 87646bc1a12ff25fe6e21875fdbb60f12483f901 [file] [log] [blame]
//===-- VEISelDAGToDAG.cpp - A dag to dag inst selector for VE ------------===//
//
// 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 VE target.
//
//===----------------------------------------------------------------------===//
#include "VE.h"
#include "VETargetMachine.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
#define DEBUG_TYPE "ve-isel"
#define PASS_NAME "VE DAG->DAG Pattern Instruction Selection"
//===--------------------------------------------------------------------===//
/// VEDAGToDAGISel - VE specific code to select VE machine
/// instructions for SelectionDAG operations.
///
namespace {
class VEDAGToDAGISel : public SelectionDAGISel {
/// Subtarget - Keep a pointer to the VE Subtarget around so that we can
/// make the right decision when generating code for different targets.
const VESubtarget *Subtarget;
public:
static char ID;
VEDAGToDAGISel() = delete;
explicit VEDAGToDAGISel(VETargetMachine &tm) : SelectionDAGISel(ID, tm) {}
bool runOnMachineFunction(MachineFunction &MF) override {
Subtarget = &MF.getSubtarget<VESubtarget>();
return SelectionDAGISel::runOnMachineFunction(MF);
}
void Select(SDNode *N) override;
// Complex Pattern Selectors.
bool selectADDRrri(SDValue N, SDValue &Base, SDValue &Index, SDValue &Offset);
bool selectADDRrii(SDValue N, SDValue &Base, SDValue &Index, SDValue &Offset);
bool selectADDRzri(SDValue N, SDValue &Base, SDValue &Index, SDValue &Offset);
bool selectADDRzii(SDValue N, SDValue &Base, SDValue &Index, SDValue &Offset);
bool selectADDRri(SDValue N, SDValue &Base, SDValue &Offset);
bool selectADDRzi(SDValue N, SDValue &Base, SDValue &Offset);
/// SelectInlineAsmMemoryOperand - Implement addressing mode selection for
/// inline asm expressions.
bool SelectInlineAsmMemoryOperand(const SDValue &Op,
InlineAsm::ConstraintCode ConstraintID,
std::vector<SDValue> &OutOps) override;
// Include the pieces autogenerated from the target description.
#include "VEGenDAGISel.inc"
private:
SDNode *getGlobalBaseReg();
bool matchADDRrr(SDValue N, SDValue &Base, SDValue &Index);
bool matchADDRri(SDValue N, SDValue &Base, SDValue &Offset);
};
} // end anonymous namespace
char VEDAGToDAGISel::ID = 0;
INITIALIZE_PASS(VEDAGToDAGISel, DEBUG_TYPE, PASS_NAME, false, false)
bool VEDAGToDAGISel::selectADDRrri(SDValue Addr, SDValue &Base, SDValue &Index,
SDValue &Offset) {
if (Addr.getOpcode() == ISD::FrameIndex)
return false;
if (Addr.getOpcode() == ISD::TargetExternalSymbol ||
Addr.getOpcode() == ISD::TargetGlobalAddress ||
Addr.getOpcode() == ISD::TargetGlobalTLSAddress)
return false; // direct calls.
SDValue LHS, RHS;
if (matchADDRri(Addr, LHS, RHS)) {
if (matchADDRrr(LHS, Base, Index)) {
Offset = RHS;
return true;
}
// Return false to try selectADDRrii.
return false;
}
if (matchADDRrr(Addr, LHS, RHS)) {
// If the input is a pair of a frame-index and a register, move a
// frame-index to LHS. This generates MI with following operands.
// %dest, #FI, %reg, offset
// In the eliminateFrameIndex, above MI is converted to the following.
// %dest, %fp, %reg, fi_offset + offset
if (isa<FrameIndexSDNode>(RHS))
std::swap(LHS, RHS);
if (matchADDRri(RHS, Index, Offset)) {
Base = LHS;
return true;
}
if (matchADDRri(LHS, Base, Offset)) {
Index = RHS;
return true;
}
Base = LHS;
Index = RHS;
Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);
return true;
}
return false; // Let the reg+imm(=0) pattern catch this!
}
bool VEDAGToDAGISel::selectADDRrii(SDValue Addr, SDValue &Base, SDValue &Index,
SDValue &Offset) {
if (matchADDRri(Addr, Base, Offset)) {
Index = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);
return true;
}
Base = Addr;
Index = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);
Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);
return true;
}
bool VEDAGToDAGISel::selectADDRzri(SDValue Addr, SDValue &Base, SDValue &Index,
SDValue &Offset) {
// Prefer ADDRrii.
return false;
}
bool VEDAGToDAGISel::selectADDRzii(SDValue Addr, SDValue &Base, SDValue &Index,
SDValue &Offset) {
if (isa<FrameIndexSDNode>(Addr))
return false;
if (Addr.getOpcode() == ISD::TargetExternalSymbol ||
Addr.getOpcode() == ISD::TargetGlobalAddress ||
Addr.getOpcode() == ISD::TargetGlobalTLSAddress)
return false; // direct calls.
if (auto *CN = dyn_cast<ConstantSDNode>(Addr)) {
if (isInt<32>(CN->getSExtValue())) {
Base = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);
Index = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);
Offset =
CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Addr), MVT::i32);
return true;
}
}
return false;
}
bool VEDAGToDAGISel::selectADDRri(SDValue Addr, SDValue &Base,
SDValue &Offset) {
if (matchADDRri(Addr, Base, Offset))
return true;
Base = Addr;
Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);
return true;
}
bool VEDAGToDAGISel::selectADDRzi(SDValue Addr, SDValue &Base,
SDValue &Offset) {
if (isa<FrameIndexSDNode>(Addr))
return false;
if (Addr.getOpcode() == ISD::TargetExternalSymbol ||
Addr.getOpcode() == ISD::TargetGlobalAddress ||
Addr.getOpcode() == ISD::TargetGlobalTLSAddress)
return false; // direct calls.
if (auto *CN = dyn_cast<ConstantSDNode>(Addr)) {
if (isInt<32>(CN->getSExtValue())) {
Base = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);
Offset =
CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Addr), MVT::i32);
return true;
}
}
return false;
}
bool VEDAGToDAGISel::matchADDRrr(SDValue Addr, SDValue &Base, SDValue &Index) {
if (isa<FrameIndexSDNode>(Addr))
return false;
if (Addr.getOpcode() == ISD::TargetExternalSymbol ||
Addr.getOpcode() == ISD::TargetGlobalAddress ||
Addr.getOpcode() == ISD::TargetGlobalTLSAddress)
return false; // direct calls.
if (Addr.getOpcode() == ISD::ADD) {
; // Nothing to do here.
} else if (Addr.getOpcode() == ISD::OR) {
// We want to look through a transform in InstCombine and DAGCombiner that
// turns 'add' into 'or', so we can treat this 'or' exactly like an 'add'.
if (!CurDAG->haveNoCommonBitsSet(Addr.getOperand(0), Addr.getOperand(1)))
return false;
} else {
return false;
}
if (Addr.getOperand(0).getOpcode() == VEISD::Lo ||
Addr.getOperand(1).getOpcode() == VEISD::Lo)
return false; // Let the LEASL patterns catch this!
Base = Addr.getOperand(0);
Index = Addr.getOperand(1);
return true;
}
bool VEDAGToDAGISel::matchADDRri(SDValue Addr, SDValue &Base, SDValue &Offset) {
auto AddrTy = Addr->getValueType(0);
if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(Addr)) {
Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), AddrTy);
Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);
return true;
}
if (Addr.getOpcode() == ISD::TargetExternalSymbol ||
Addr.getOpcode() == ISD::TargetGlobalAddress ||
Addr.getOpcode() == ISD::TargetGlobalTLSAddress)
return false; // direct calls.
if (CurDAG->isBaseWithConstantOffset(Addr)) {
ConstantSDNode *CN = cast<ConstantSDNode>(Addr.getOperand(1));
if (isInt<32>(CN->getSExtValue())) {
if (FrameIndexSDNode *FIN =
dyn_cast<FrameIndexSDNode>(Addr.getOperand(0))) {
// Constant offset from frame ref.
Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), AddrTy);
} else {
Base = Addr.getOperand(0);
}
Offset =
CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Addr), MVT::i32);
return true;
}
}
return false;
}
void VEDAGToDAGISel::Select(SDNode *N) {
SDLoc dl(N);
if (N->isMachineOpcode()) {
N->setNodeId(-1);
return; // Already selected.
}
switch (N->getOpcode()) {
// Late eliminate the LEGALAVL wrapper
case VEISD::LEGALAVL:
ReplaceNode(N, N->getOperand(0).getNode());
return;
// Lower (broadcast 1) and (broadcast 0) to VM[P]0
case VEISD::VEC_BROADCAST: {
MVT SplatResTy = N->getSimpleValueType(0);
if (SplatResTy.getVectorElementType() != MVT::i1)
break;
// Constant non-zero broadcast.
auto BConst = dyn_cast<ConstantSDNode>(N->getOperand(0));
if (!BConst)
break;
bool BCTrueMask = (BConst->getSExtValue() != 0);
if (!BCTrueMask)
break;
// Packed or non-packed.
SDValue New;
if (SplatResTy.getVectorNumElements() == StandardVectorWidth) {
New = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), SDLoc(N), VE::VM0,
MVT::v256i1);
} else if (SplatResTy.getVectorNumElements() == PackedVectorWidth) {
New = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), SDLoc(N), VE::VMP0,
MVT::v512i1);
} else
break;
// Replace.
ReplaceNode(N, New.getNode());
return;
}
case VEISD::GLOBAL_BASE_REG:
ReplaceNode(N, getGlobalBaseReg());
return;
}
SelectCode(N);
}
/// SelectInlineAsmMemoryOperand - Implement addressing mode selection for
/// inline asm expressions.
bool VEDAGToDAGISel::SelectInlineAsmMemoryOperand(
const SDValue &Op, InlineAsm::ConstraintCode ConstraintID,
std::vector<SDValue> &OutOps) {
SDValue Op0, Op1;
switch (ConstraintID) {
default:
llvm_unreachable("Unexpected asm memory constraint");
case InlineAsm::ConstraintCode::o:
case InlineAsm::ConstraintCode::m: // memory
// Try to match ADDRri since reg+imm style is safe for all VE instructions
// with a memory operand.
if (selectADDRri(Op, Op0, Op1)) {
OutOps.push_back(Op0);
OutOps.push_back(Op1);
return false;
}
// Otherwise, require the address to be in a register and immediate 0.
OutOps.push_back(Op);
OutOps.push_back(CurDAG->getTargetConstant(0, SDLoc(Op), MVT::i32));
return false;
}
return true;
}
SDNode *VEDAGToDAGISel::getGlobalBaseReg() {
Register GlobalBaseReg = Subtarget->getInstrInfo()->getGlobalBaseReg(MF);
return CurDAG
->getRegister(GlobalBaseReg, TLI->getPointerTy(CurDAG->getDataLayout()))
.getNode();
}
/// createVEISelDag - This pass converts a legalized DAG into a
/// VE-specific DAG, ready for instruction scheduling.
///
FunctionPass *llvm::createVEISelDag(VETargetMachine &TM) {
return new VEDAGToDAGISel(TM);
}