
This commit introduces support for materialising 64-bit constants for RV64I, making use of the RISCVMatInt::generateInstSeq helper in order to share logic for immediate materialisation with the MC layer (where it's used for the li pseudoinstruction). test/CodeGen/RISCV/imm.ll is updated to test RV64, and gains new 64-bit constant tests. It would be preferable if anyext constant returns were sign rather than zero extended (see PR39092). This patch simply adds an explicit signext to the returns in imm.ll. Further optimisations for constant materialisation are possible, most notably for mask-like values which can be generated my loading -1 and shifting right. A future patch will standardise on the C++ codepath for immediate selection on RV32 as well as RV64, and then add further such optimisations to RISCVMatInt::generateInstSeq in order to benefit both RV32 and RV64 for codegen and li expansion. Differential Revision: https://reviews.llvm.org/D52962 llvm-svn: 347042
249 lines
7.5 KiB
C++
249 lines
7.5 KiB
C++
//===-- RISCVISelDAGToDAG.cpp - A dag to dag inst selector for RISCV ------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines an instruction selector for the RISCV target.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "MCTargetDesc/RISCVMCTargetDesc.h"
|
|
#include "RISCV.h"
|
|
#include "RISCVTargetMachine.h"
|
|
#include "Utils/RISCVMatInt.h"
|
|
#include "llvm/CodeGen/MachineFrameInfo.h"
|
|
#include "llvm/CodeGen/SelectionDAGISel.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/MathExtras.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "riscv-isel"
|
|
|
|
// RISCV-specific code to select RISCV machine instructions for
|
|
// SelectionDAG operations.
|
|
namespace {
|
|
class RISCVDAGToDAGISel final : public SelectionDAGISel {
|
|
const RISCVSubtarget *Subtarget;
|
|
|
|
public:
|
|
explicit RISCVDAGToDAGISel(RISCVTargetMachine &TargetMachine)
|
|
: SelectionDAGISel(TargetMachine) {}
|
|
|
|
StringRef getPassName() const override {
|
|
return "RISCV DAG->DAG Pattern Instruction Selection";
|
|
}
|
|
|
|
bool runOnMachineFunction(MachineFunction &MF) override {
|
|
Subtarget = &MF.getSubtarget<RISCVSubtarget>();
|
|
return SelectionDAGISel::runOnMachineFunction(MF);
|
|
}
|
|
|
|
void PostprocessISelDAG() override;
|
|
|
|
void Select(SDNode *Node) override;
|
|
|
|
bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
|
|
std::vector<SDValue> &OutOps) override;
|
|
|
|
bool SelectAddrFI(SDValue Addr, SDValue &Base);
|
|
|
|
// Include the pieces autogenerated from the target description.
|
|
#include "RISCVGenDAGISel.inc"
|
|
|
|
private:
|
|
void doPeepholeLoadStoreADDI();
|
|
};
|
|
}
|
|
|
|
void RISCVDAGToDAGISel::PostprocessISelDAG() {
|
|
doPeepholeLoadStoreADDI();
|
|
}
|
|
|
|
static SDNode *selectImm(SelectionDAG *CurDAG, const SDLoc &DL, int64_t Imm,
|
|
MVT XLenVT) {
|
|
RISCVMatInt::InstSeq Seq;
|
|
RISCVMatInt::generateInstSeq(Imm, XLenVT == MVT::i64, Seq);
|
|
|
|
SDNode *Result;
|
|
SDValue SrcReg = CurDAG->getRegister(RISCV::X0, XLenVT);
|
|
for (RISCVMatInt::Inst &Inst : Seq) {
|
|
SDValue SDImm = CurDAG->getTargetConstant(Inst.Imm, DL, XLenVT);
|
|
if (Inst.Opc == RISCV::LUI)
|
|
Result = CurDAG->getMachineNode(RISCV::LUI, DL, XLenVT, SDImm);
|
|
else
|
|
Result = CurDAG->getMachineNode(Inst.Opc, DL, XLenVT, SrcReg, SDImm);
|
|
|
|
// Only the first instruction has X0 as its source.
|
|
SrcReg = SDValue(Result, 0);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
void RISCVDAGToDAGISel::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 XLenVT = Subtarget->getXLenVT();
|
|
SDLoc DL(Node);
|
|
EVT VT = Node->getValueType(0);
|
|
|
|
switch (Opcode) {
|
|
case ISD::Constant: {
|
|
auto ConstNode = cast<ConstantSDNode>(Node);
|
|
if (VT == XLenVT && ConstNode->isNullValue()) {
|
|
SDValue New = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), SDLoc(Node),
|
|
RISCV::X0, XLenVT);
|
|
ReplaceNode(Node, New.getNode());
|
|
return;
|
|
}
|
|
int64_t Imm = ConstNode->getSExtValue();
|
|
if (XLenVT == MVT::i64) {
|
|
ReplaceNode(Node, selectImm(CurDAG, SDLoc(Node), Imm, XLenVT));
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case ISD::FrameIndex: {
|
|
SDValue Imm = CurDAG->getTargetConstant(0, DL, XLenVT);
|
|
int FI = cast<FrameIndexSDNode>(Node)->getIndex();
|
|
SDValue TFI = CurDAG->getTargetFrameIndex(FI, VT);
|
|
ReplaceNode(Node, CurDAG->getMachineNode(RISCV::ADDI, DL, VT, TFI, Imm));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Select the default instruction.
|
|
SelectCode(Node);
|
|
}
|
|
|
|
bool RISCVDAGToDAGISel::SelectInlineAsmMemoryOperand(
|
|
const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) {
|
|
switch (ConstraintID) {
|
|
case InlineAsm::Constraint_i:
|
|
case InlineAsm::Constraint_m:
|
|
// We just support simple memory operands that have a single address
|
|
// operand and need no special handling.
|
|
OutOps.push_back(Op);
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RISCVDAGToDAGISel::SelectAddrFI(SDValue Addr, SDValue &Base) {
|
|
if (auto FIN = dyn_cast<FrameIndexSDNode>(Addr)) {
|
|
Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), Subtarget->getXLenVT());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Merge an ADDI into the offset of a load/store instruction where possible.
|
|
// (load (add base, off), 0) -> (load base, off)
|
|
// (store val, (add base, off)) -> (store val, base, off)
|
|
void RISCVDAGToDAGISel::doPeepholeLoadStoreADDI() {
|
|
SelectionDAG::allnodes_iterator Position(CurDAG->getRoot().getNode());
|
|
++Position;
|
|
|
|
while (Position != CurDAG->allnodes_begin()) {
|
|
SDNode *N = &*--Position;
|
|
// Skip dead nodes and any non-machine opcodes.
|
|
if (N->use_empty() || !N->isMachineOpcode())
|
|
continue;
|
|
|
|
int OffsetOpIdx;
|
|
int BaseOpIdx;
|
|
|
|
// Only attempt this optimisation for I-type loads and S-type stores.
|
|
switch (N->getMachineOpcode()) {
|
|
default:
|
|
continue;
|
|
case RISCV::LB:
|
|
case RISCV::LH:
|
|
case RISCV::LW:
|
|
case RISCV::LBU:
|
|
case RISCV::LHU:
|
|
case RISCV::LWU:
|
|
case RISCV::LD:
|
|
case RISCV::FLW:
|
|
case RISCV::FLD:
|
|
BaseOpIdx = 0;
|
|
OffsetOpIdx = 1;
|
|
break;
|
|
case RISCV::SB:
|
|
case RISCV::SH:
|
|
case RISCV::SW:
|
|
case RISCV::SD:
|
|
case RISCV::FSW:
|
|
case RISCV::FSD:
|
|
BaseOpIdx = 1;
|
|
OffsetOpIdx = 2;
|
|
break;
|
|
}
|
|
|
|
// Currently, the load/store offset must be 0 to be considered for this
|
|
// peephole optimisation.
|
|
if (!isa<ConstantSDNode>(N->getOperand(OffsetOpIdx)) ||
|
|
N->getConstantOperandVal(OffsetOpIdx) != 0)
|
|
continue;
|
|
|
|
SDValue Base = N->getOperand(BaseOpIdx);
|
|
|
|
// If the base is an ADDI, we can merge it in to the load/store.
|
|
if (!Base.isMachineOpcode() || Base.getMachineOpcode() != RISCV::ADDI)
|
|
continue;
|
|
|
|
SDValue ImmOperand = Base.getOperand(1);
|
|
|
|
if (auto Const = dyn_cast<ConstantSDNode>(ImmOperand)) {
|
|
ImmOperand = CurDAG->getTargetConstant(
|
|
Const->getSExtValue(), SDLoc(ImmOperand), ImmOperand.getValueType());
|
|
} else if (auto GA = dyn_cast<GlobalAddressSDNode>(ImmOperand)) {
|
|
ImmOperand = CurDAG->getTargetGlobalAddress(
|
|
GA->getGlobal(), SDLoc(ImmOperand), ImmOperand.getValueType(),
|
|
GA->getOffset(), GA->getTargetFlags());
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
LLVM_DEBUG(dbgs() << "Folding add-immediate into mem-op:\nBase: ");
|
|
LLVM_DEBUG(Base->dump(CurDAG));
|
|
LLVM_DEBUG(dbgs() << "\nN: ");
|
|
LLVM_DEBUG(N->dump(CurDAG));
|
|
LLVM_DEBUG(dbgs() << "\n");
|
|
|
|
// Modify the offset operand of the load/store.
|
|
if (BaseOpIdx == 0) // Load
|
|
CurDAG->UpdateNodeOperands(N, Base.getOperand(0), ImmOperand,
|
|
N->getOperand(2));
|
|
else // Store
|
|
CurDAG->UpdateNodeOperands(N, N->getOperand(0), Base.getOperand(0),
|
|
ImmOperand, N->getOperand(3));
|
|
|
|
// The add-immediate may now be dead, in which case remove it.
|
|
if (Base.getNode()->use_empty())
|
|
CurDAG->RemoveDeadNode(Base.getNode());
|
|
}
|
|
}
|
|
|
|
// This pass converts a legalized DAG into a RISCV-specific DAG, ready
|
|
// for instruction scheduling.
|
|
FunctionPass *llvm::createRISCVISelDag(RISCVTargetMachine &TM) {
|
|
return new RISCVDAGToDAGISel(TM);
|
|
}
|