Previously we had a shared ID in SelectionDAGISel. AMDGPU has an initializePass function for its subclass of SelectionDAGISel. No other target does. This causes all target specific SelectionDAGISel passes to be known as "amdgpu-isel". I'm not sure what would happen if another target tried to implement an initializePass function too since the ID is already claimed. This patch gives all targets their own ID and passes it down to SelectionDAGISel constructor to MachineFunctionPass's constructor. Unfortunately, I think this causes most targets to lose print-before/after-all support for their SelectionDAGISel pass. And they probably no longer support start/stop-before/after. We can add initializePass functions to fix this as a follow up. NOTE: This was probably also broken if the AMDGPU target isn't compiled in. Step 1 to fixing PR59538. Reviewed By: arsenm Differential Revision: https://reviews.llvm.org/D140161
339 lines
11 KiB
C++
339 lines
11 KiB
C++
//===-- 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;
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
/// 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;
|
|
|
|
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,
|
|
unsigned ConstraintID,
|
|
std::vector<SDValue> &OutOps) override;
|
|
|
|
StringRef getPassName() const override {
|
|
return "VE DAG->DAG Pattern Instruction Selection";
|
|
}
|
|
|
|
// 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;
|
|
|
|
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,
|
|
unsigned ConstraintID,
|
|
std::vector<SDValue> &OutOps) {
|
|
SDValue Op0, Op1;
|
|
switch (ConstraintID) {
|
|
default:
|
|
llvm_unreachable("Unexpected asm memory constraint");
|
|
case InlineAsm::Constraint_o:
|
|
case InlineAsm::Constraint_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);
|
|
}
|