Skip to content

[SelectionDAG] Wire up -gen-sdnode-info TableGen backend #125358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions llvm/include/llvm/CodeGen/SDNodeInfo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//==------------------------------------------------------------------------==//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CODEGEN_SDNODEINFO_H
#define LLVM_CODEGEN_SDNODEINFO_H

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringTable.h"
#include "llvm/CodeGen/ISDOpcodes.h"
#include "llvm/CodeGenTypes/MachineValueType.h"

namespace llvm {

class SDNode;
class SelectionDAG;

enum SDNP {
SDNPHasChain,
SDNPOutGlue,
SDNPInGlue,
SDNPOptInGlue,
SDNPMemOperand,
SDNPVariadic,
};

enum SDTC : uint8_t {
SDTCisVT,
SDTCisPtrTy,
SDTCisInt,
SDTCisFP,
SDTCisVec,
SDTCisSameAs,
SDTCisVTSmallerThanOp,
SDTCisOpSmallerThanOp,
SDTCisEltOfVec,
SDTCisSubVecOfVec,
SDTCVecEltisVT,
SDTCisSameNumEltsAs,
SDTCisSameSizeAs,
};

enum SDNF {
SDNFIsStrictFP,
};

struct SDTypeConstraint {
SDTC Kind;
uint8_t OpNo;
uint8_t OtherOpNo;
MVT::SimpleValueType VT;
};

struct SDNodeDesc {
uint16_t NumResults;
int16_t NumOperands;
uint32_t Properties;
uint32_t Flags;
uint32_t TSFlags;
unsigned NameOffset;
unsigned ConstraintOffset;
unsigned ConstraintCount;

bool hasProperty(SDNP Property) const { return Properties & (1 << Property); }

bool hasFlag(SDNF Flag) const { return Flags & (1 << Flag); }
};

class SDNodeInfo final {
unsigned NumOpcodes;
const SDNodeDesc *Descs;
StringTable Names;
const SDTypeConstraint *Constraints;

public:
constexpr SDNodeInfo(unsigned NumOpcodes, const SDNodeDesc *Descs,
StringTable Names, const SDTypeConstraint *Constraints)
: NumOpcodes(NumOpcodes), Descs(Descs), Names(Names),
Constraints(Constraints) {}

/// Returns true if there is a generated description for a node with the given
/// target-specific opcode.
bool hasDesc(unsigned Opcode) const {
assert(Opcode >= ISD::BUILTIN_OP_END && "Expected target-specific opcode");
return Opcode < ISD::BUILTIN_OP_END + NumOpcodes;
}

/// Returns the description of a node with the given opcode.
const SDNodeDesc &getDesc(unsigned Opcode) const {
assert(hasDesc(Opcode));
return Descs[Opcode - ISD::BUILTIN_OP_END];
}

/// Returns operand constraints for a node with the given opcode.
ArrayRef<SDTypeConstraint> getConstraints(unsigned Opcode) const {
const SDNodeDesc &Desc = getDesc(Opcode);
return ArrayRef(&Constraints[Desc.ConstraintOffset], Desc.ConstraintCount);
}

/// Returns the name of the given target-specific opcode, suitable for
/// debug printing.
StringRef getName(unsigned Opcode) const {
return Names[getDesc(Opcode).NameOffset];
}

void verifyNode(const SelectionDAG &DAG, const SDNode *N) const;
};

} // namespace llvm

#endif // LLVM_CODEGEN_SDNODEINFO_H
3 changes: 3 additions & 0 deletions llvm/include/llvm/CodeGen/SelectionDAG.h
Original file line number Diff line number Diff line change
Expand Up @@ -2445,6 +2445,9 @@ class SelectionDAG {
const SDLoc &DLoc);

private:
#ifndef NDEBUG
void verifyNode(SDNode *N) const;
#endif
void InsertNode(SDNode *N);
bool RemoveNodeFromCSEMaps(SDNode *N);
void AddModifiedNodeToCSEMaps(SDNode *N);
Expand Down
48 changes: 48 additions & 0 deletions llvm/include/llvm/CodeGen/SelectionDAGTargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#define LLVM_CODEGEN_SELECTIONDAGTARGETINFO_H

#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/CodeGen/SDNodeInfo.h"
#include "llvm/CodeGen/SelectionDAGNodes.h"
#include "llvm/Support/CodeGen.h"
#include <utility>
Expand All @@ -35,6 +36,12 @@ class SelectionDAGTargetInfo {
SelectionDAGTargetInfo &operator=(const SelectionDAGTargetInfo &) = delete;
virtual ~SelectionDAGTargetInfo();

/// Returns the name of the given target-specific opcode, suitable for
/// debug printing.
virtual const char *getTargetNodeName(unsigned Opcode) const {
return nullptr;
}

/// Returns true if a node with the given target-specific opcode has
/// a memory operand. Nodes with such opcodes can only be created with
/// `SelectionDAG::getMemIntrinsicNode`.
Expand All @@ -48,6 +55,10 @@ class SelectionDAGTargetInfo {
/// may raise a floating-point exception.
virtual bool mayRaiseFPException(unsigned Opcode) const;

/// Checks that the given target-specific node is valid. Aborts if it is not.
virtual void verifyTargetNode(const SelectionDAG &DAG,
const SDNode *N) const {}

/// Emit target-specific code that performs a memcpy.
/// This can be used by targets to provide code sequences for cases
/// that don't fit the target's parameters for simple loads/stores and can be
Expand Down Expand Up @@ -176,6 +187,43 @@ class SelectionDAGTargetInfo {
}
};

/// Proxy class that targets should inherit from if they wish to use
/// the generated node descriptions.
class SelectionDAGGenTargetInfo : public SelectionDAGTargetInfo {
protected:
const SDNodeInfo &GenNodeInfo;

explicit SelectionDAGGenTargetInfo(const SDNodeInfo &GenNodeInfo)
: GenNodeInfo(GenNodeInfo) {}

public:
~SelectionDAGGenTargetInfo() override;

const char *getTargetNodeName(unsigned Opcode) const override {
assert(GenNodeInfo.hasDesc(Opcode) &&
"The name should be provided by the derived class");
return GenNodeInfo.getName(Opcode).data();
}

bool isTargetMemoryOpcode(unsigned Opcode) const override {
if (GenNodeInfo.hasDesc(Opcode))
return GenNodeInfo.getDesc(Opcode).hasProperty(SDNPMemOperand);
return false;
}

bool isTargetStrictFPOpcode(unsigned Opcode) const override {
if (GenNodeInfo.hasDesc(Opcode))
return GenNodeInfo.getDesc(Opcode).hasFlag(SDNFIsStrictFP);
return false;
}

void verifyTargetNode(const SelectionDAG &DAG,
const SDNode *N) const override {
if (GenNodeInfo.hasDesc(N->getOpcode()))
GenNodeInfo.verifyNode(DAG, N);
}
};

} // end namespace llvm

#endif // LLVM_CODEGEN_SELECTIONDAGTARGETINFO_H
5 changes: 0 additions & 5 deletions llvm/include/llvm/CodeGen/TargetLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -4989,11 +4989,6 @@ class TargetLowering : public TargetLoweringBase {
bool verifyReturnAddressArgumentIsConstant(SDValue Op,
SelectionDAG &DAG) const;

#ifndef NDEBUG
/// Check the given SDNode. Aborts if it is invalid.
virtual void verifyTargetSDNode(const SDNode *N) const {};
#endif

//===--------------------------------------------------------------------===//
// Inline Asm Support hooks
//
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/SelectionDAG/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ add_llvm_component_library(LLVMSelectionDAG
LegalizeVectorOps.cpp
LegalizeVectorTypes.cpp
ResourcePriorityQueue.cpp
SDNodeInfo.cpp
ScheduleDAGFast.cpp
ScheduleDAGRRList.cpp
ScheduleDAGSDNodes.cpp
Expand Down
128 changes: 128 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
//==------------------------------------------------------------------------==//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "llvm/CodeGen/SDNodeInfo.h"
#include "llvm/CodeGen/SelectionDAGNodes.h"

using namespace llvm;

static void reportNodeError(const SelectionDAG &DAG, const SDNode *N,
const Twine &Msg) {
std::string S;
raw_string_ostream SS(S);
SS << "invalid node: " << Msg << '\n';
N->printrWithDepth(SS, &DAG, 2);
report_fatal_error(StringRef(S));
}

static void checkResultType(const SelectionDAG &DAG, const SDNode *N,
unsigned ResIdx, EVT ExpectedVT) {
EVT ActualVT = N->getValueType(ResIdx);
if (ActualVT != ExpectedVT)
reportNodeError(
DAG, N,
"result #" + Twine(ResIdx) + " has invalid type; expected " +
ExpectedVT.getEVTString() + ", got " + ActualVT.getEVTString());
}

static void checkOperandType(const SelectionDAG &DAG, const SDNode *N,
unsigned OpIdx, EVT ExpectedVT) {
EVT ActualVT = N->getOperand(OpIdx).getValueType();
if (ActualVT != ExpectedVT)
reportNodeError(
DAG, N,
"operand #" + Twine(OpIdx) + " has invalid type; expected " +
ExpectedVT.getEVTString() + ", got " + ActualVT.getEVTString());
}

void SDNodeInfo::verifyNode(const SelectionDAG &DAG, const SDNode *N) const {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently this only verifies basic SDNode "prototype" -- the number of operands/results and the types of chain/glue operands/results. I refrained from making use of the imported SDTypeProfile in this patch to keep it smaller (and I need to spend more time on implementing proper type profile checking anyway).

const SDNodeDesc &Desc = getDesc(N->getOpcode());
bool HasChain = Desc.hasProperty(SDNPHasChain);
bool HasOutGlue = Desc.hasProperty(SDNPOutGlue);
bool HasInGlue = Desc.hasProperty(SDNPInGlue);
bool HasOptInGlue = Desc.hasProperty(SDNPOptInGlue);
bool IsVariadic = Desc.hasProperty(SDNPVariadic);

unsigned ActualNumResults = N->getNumValues();
unsigned ExpectedNumResults = Desc.NumResults + HasChain + HasOutGlue;

if (ActualNumResults != ExpectedNumResults)
reportNodeError(DAG, N,
"invalid number of results; expected " +
Twine(ExpectedNumResults) + ", got " +
Twine(ActualNumResults));

// Chain result comes after all normal results.
if (HasChain) {
unsigned ChainResIdx = Desc.NumResults;
checkResultType(DAG, N, ChainResIdx, MVT::Other);
}

// Glue result comes last.
if (HasOutGlue) {
unsigned GlueResIdx = Desc.NumResults + HasChain;
checkResultType(DAG, N, GlueResIdx, MVT::Glue);
}

// In the most general case, the operands of a node go in the following order:
// chain, fix#0, ..., fix#M-1, var#0, ... var#N-1, glue
// If the number of operands is < 0, M can be any;
// If the node has SDNPVariadic property, N can be any.
bool HasOptionalOperands = Desc.NumOperands < 0 || IsVariadic;

unsigned ActualNumOperands = N->getNumOperands();
unsigned ExpectedMinNumOperands =
(Desc.NumOperands >= 0 ? Desc.NumOperands : 0) + HasChain + HasInGlue;

// Check the lower bound.
if (ActualNumOperands < ExpectedMinNumOperands) {
StringRef How = HasOptionalOperands ? "at least " : "";
reportNodeError(DAG, N,
"invalid number of operands; expected " + How +
Twine(ExpectedMinNumOperands) + ", got " +
Twine(ActualNumOperands));
}

// Check the upper bound. We can only do this if the number of fixed operands
// is known and there are no variadic operands.
if (Desc.NumOperands >= 0 && !IsVariadic) {
// Account for optional input glue.
unsigned ExpectedMaxNumOperands = ExpectedMinNumOperands + HasOptInGlue;
if (ActualNumOperands > ExpectedMaxNumOperands) {
StringRef How = HasOptInGlue ? "at most " : "";
reportNodeError(DAG, N,
"invalid number of operands; expected " + How +
Twine(ExpectedMaxNumOperands) + ", got " +
Twine(ActualNumOperands));
}
}

// Chain operand comes first.
if (HasChain)
checkOperandType(DAG, N, 0, MVT::Other);

// Glue operand comes last.
if (HasInGlue)
checkOperandType(DAG, N, ActualNumOperands - 1, MVT::Glue);
if (HasOptInGlue && ActualNumOperands >= 1 &&
N->getOperand(ActualNumOperands - 1).getValueType() == MVT::Glue)
HasInGlue = true;

// Check variadic operands. These should be Register or RegisterMask.
if (IsVariadic && Desc.NumOperands >= 0) {
unsigned VarOpStart = HasChain + Desc.NumOperands;
unsigned VarOpEnd = ActualNumOperands - HasInGlue;
for (unsigned OpIdx = VarOpStart; OpIdx != VarOpEnd; ++OpIdx) {
unsigned OpOpcode = N->getOperand(OpIdx).getOpcode();
if (OpOpcode != ISD::Register && OpOpcode != ISD::RegisterMask)
reportNodeError(DAG, N,
"variadic operand #" + Twine(OpIdx) +
" must be Register or RegisterMask");
}
}
}
8 changes: 4 additions & 4 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1146,11 +1146,11 @@ void SelectionDAG::DeallocateNode(SDNode *N) {

#ifndef NDEBUG
/// VerifySDNode - Check the given SDNode. Aborts if it is invalid.
static void VerifySDNode(SDNode *N, const TargetLowering *TLI) {
void SelectionDAG::verifyNode(SDNode *N) const {
switch (N->getOpcode()) {
default:
if (N->getOpcode() > ISD::BUILTIN_OP_END)
TLI->verifyTargetSDNode(N);
if (N->isTargetOpcode())
getSelectionDAGInfo().verifyTargetNode(*this, N);
break;
case ISD::BUILD_PAIR: {
EVT VT = N->getValueType(0);
Expand Down Expand Up @@ -1194,7 +1194,7 @@ void SelectionDAG::InsertNode(SDNode *N) {
AllNodes.push_back(N);
#ifndef NDEBUG
N->PersistentId = NextPersistentId++;
VerifySDNode(N, TLI);
verifyNode(N);
#endif
for (DAGUpdateListener *DUL = UpdateListeners; DUL; DUL = DUL->Next)
DUL->NodeInserted(N);
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/CodeGen/SelectionDAGNodes.h"
#include "llvm/CodeGen/SelectionDAGTargetInfo.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
Expand Down Expand Up @@ -67,6 +68,9 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const {
return "<<Unknown Machine Node #" + utostr(getOpcode()) + ">>";
}
if (G) {
const SelectionDAGTargetInfo &TSI = G->getSelectionDAGInfo();
if (const char *Name = TSI.getTargetNodeName(getOpcode()))
return Name;
const TargetLowering &TLI = G->getTargetLoweringInfo();
const char *Name = TLI.getTargetNodeName(getOpcode());
if (Name) return Name;
Expand Down
Loading