Support atomic exchange and atomic compare and exchange instructions. Change CAS and TS1AM instructions for ISel patterns. Add selectADDRzi pattern for them. Add TS1AM pseudo instruction also for better ISel. Add shouldExpandAtomicRMWInIR() function to expand all atomicrmw instructions except atomicrmw xchg. Add custom lower for i8/i16 atomicrmw xchg. Modify replaceFI to support CAS/TS1AM instructions which use "reg+disp" operands instead of "reg+imm+disp" operands. And, add several regression tests to check the correctness. Reviewed By: simoll Differential Revision: https://reviews.llvm.org/D93161
360 lines
11 KiB
C++
360 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 "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;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Instruction Selector Implementation
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Convert a DAG integer condition code to a VE ICC condition.
|
|
inline static VECC::CondCode intCondCode2Icc(ISD::CondCode CC) {
|
|
switch (CC) {
|
|
default:
|
|
llvm_unreachable("Unknown integer condition code!");
|
|
case ISD::SETEQ:
|
|
return VECC::CC_IEQ;
|
|
case ISD::SETNE:
|
|
return VECC::CC_INE;
|
|
case ISD::SETLT:
|
|
return VECC::CC_IL;
|
|
case ISD::SETGT:
|
|
return VECC::CC_IG;
|
|
case ISD::SETLE:
|
|
return VECC::CC_ILE;
|
|
case ISD::SETGE:
|
|
return VECC::CC_IGE;
|
|
case ISD::SETULT:
|
|
return VECC::CC_IL;
|
|
case ISD::SETULE:
|
|
return VECC::CC_ILE;
|
|
case ISD::SETUGT:
|
|
return VECC::CC_IG;
|
|
case ISD::SETUGE:
|
|
return VECC::CC_IGE;
|
|
}
|
|
}
|
|
|
|
/// Convert a DAG floating point condition code to a VE FCC condition.
|
|
inline static VECC::CondCode fpCondCode2Fcc(ISD::CondCode CC) {
|
|
switch (CC) {
|
|
default:
|
|
llvm_unreachable("Unknown fp condition code!");
|
|
case ISD::SETFALSE:
|
|
return VECC::CC_AF;
|
|
case ISD::SETEQ:
|
|
case ISD::SETOEQ:
|
|
return VECC::CC_EQ;
|
|
case ISD::SETNE:
|
|
case ISD::SETONE:
|
|
return VECC::CC_NE;
|
|
case ISD::SETLT:
|
|
case ISD::SETOLT:
|
|
return VECC::CC_L;
|
|
case ISD::SETGT:
|
|
case ISD::SETOGT:
|
|
return VECC::CC_G;
|
|
case ISD::SETLE:
|
|
case ISD::SETOLE:
|
|
return VECC::CC_LE;
|
|
case ISD::SETGE:
|
|
case ISD::SETOGE:
|
|
return VECC::CC_GE;
|
|
case ISD::SETO:
|
|
return VECC::CC_NUM;
|
|
case ISD::SETUO:
|
|
return VECC::CC_NAN;
|
|
case ISD::SETUEQ:
|
|
return VECC::CC_EQNAN;
|
|
case ISD::SETUNE:
|
|
return VECC::CC_NENAN;
|
|
case ISD::SETULT:
|
|
return VECC::CC_LNAN;
|
|
case ISD::SETUGT:
|
|
return VECC::CC_GNAN;
|
|
case ISD::SETULE:
|
|
return VECC::CC_LENAN;
|
|
case ISD::SETUGE:
|
|
return VECC::CC_GENAN;
|
|
case ISD::SETTRUE:
|
|
return VECC::CC_AT;
|
|
}
|
|
}
|
|
|
|
/// getImmVal - get immediate representation of integer value
|
|
inline static uint64_t getImmVal(const ConstantSDNode *N) {
|
|
return N->getSExtValue();
|
|
}
|
|
|
|
/// getFpImmVal - get immediate representation of floating point value
|
|
inline static uint64_t getFpImmVal(const ConstantFPSDNode *N) {
|
|
const APInt &Imm = N->getValueAPF().bitcastToAPInt();
|
|
uint64_t Val = Imm.getZExtValue();
|
|
if (Imm.getBitWidth() == 32) {
|
|
// Immediate value of float place places at higher bits on VE.
|
|
Val <<= 32;
|
|
}
|
|
return Val;
|
|
}
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
/// 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:
|
|
explicit VEDAGToDAGISel(VETargetMachine &tm) : SelectionDAGISel(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);
|
|
|
|
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
|
|
|
|
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 (dyn_cast<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 (dyn_cast<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 (dyn_cast<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 (dyn_cast<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()) {
|
|
case VEISD::GLOBAL_BASE_REG:
|
|
ReplaceNode(N, getGlobalBaseReg());
|
|
return;
|
|
}
|
|
|
|
SelectCode(N);
|
|
}
|
|
|
|
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);
|
|
}
|