48 changes: 3 additions & 45 deletions llvm/include/llvm/TableGen/Record.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,6 @@ class Init {
IK_AnonymousNameInit,
IK_StringInit,
IK_VarInit,
IK_VarListElementInit,
IK_VarBitInit,
IK_VarDefInit,
IK_LastTypedInit,
Expand Down Expand Up @@ -384,14 +383,6 @@ class Init {
return nullptr;
}

/// This function is used to implement the list slice
/// selection operator. Given a value, it selects the specified list
/// elements, returning them as a new \p Init of type \p list. If it
/// is not legal to use the slice operator, null is returned.
virtual Init *convertInitListSlice(ArrayRef<unsigned> Elements) const {
return nullptr;
}

/// This function is used to implement the FieldInit class.
/// Implementors of this method should return the type of the named
/// field if they are of type record.
Expand Down Expand Up @@ -443,7 +434,6 @@ class TypedInit : public Init {
Init *convertInitializerTo(RecTy *Ty) const override;

Init *convertInitializerBitRange(ArrayRef<unsigned> Bits) const override;
Init *convertInitListSlice(ArrayRef<unsigned> Elements) const override;

/// This method is used to implement the FieldInit class.
/// Implementors of this method should return the type of the named field if
Expand Down Expand Up @@ -724,8 +714,6 @@ class ListInit final : public TypedInit, public FoldingSetNode,

Record *getElementAsRecord(unsigned i) const;

Init *convertInitListSlice(ArrayRef<unsigned> Elements) const override;

Init *convertInitializerTo(RecTy *Ty) const override;

/// This method is used by classes that refer to other
Expand Down Expand Up @@ -857,7 +845,10 @@ class BinOpInit : public OpInit, public FoldingSetNode {
LISTCONCAT,
LISTSPLAT,
LISTREMOVE,
LISTELEM,
LISTSLICE,
RANGE,
RANGEC,
STRCONCAT,
INTERLEAVE,
CONCAT,
Expand Down Expand Up @@ -1239,39 +1230,6 @@ class VarBitInit final : public TypedInit {
}
};

/// List[4] - Represent access to one element of a var or
/// field.
class VarListElementInit : public TypedInit {
TypedInit *TI;
unsigned Element;

VarListElementInit(TypedInit *T, unsigned E)
: TypedInit(IK_VarListElementInit,
cast<ListRecTy>(T->getType())->getElementType()),
TI(T), Element(E) {
assert(T->getType() && isa<ListRecTy>(T->getType()) &&
"Illegal VarBitInit expression!");
}

public:
VarListElementInit(const VarListElementInit &) = delete;
VarListElementInit &operator=(const VarListElementInit &) = delete;

static bool classof(const Init *I) {
return I->getKind() == IK_VarListElementInit;
}

static VarListElementInit *get(TypedInit *T, unsigned E);

TypedInit *getVariable() const { return TI; }
unsigned getElementNum() const { return Element; }

std::string getAsString() const override;
Init *resolveReferences(Resolver &R) const override;

Init *getBit(unsigned Bit) const override;
};

/// AL - Represent a reference to a 'def' in the description
class DefInit : public TypedInit {
friend class Record;
Expand Down
118 changes: 50 additions & 68 deletions llvm/lib/TableGen/Record.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,6 @@ struct RecordKeeperImpl {
FoldingSet<ExistsOpInit> TheExistsOpInitPool;
DenseMap<std::pair<RecTy *, Init *>, VarInit *> TheVarInitPool;
DenseMap<std::pair<TypedInit *, unsigned>, VarBitInit *> TheVarBitInitPool;
DenseMap<std::pair<TypedInit *, unsigned>, VarListElementInit *>
TheVarListElementInitPool;
FoldingSet<VarDefInit> TheVarDefInitPool;
DenseMap<std::pair<Init *, StringInit *>, FieldInit *> TheFieldInitPool;
FoldingSet<CondOpInit> TheCondOpInitPool;
Expand Down Expand Up @@ -670,23 +668,6 @@ Init *ListInit::convertInitializerTo(RecTy *Ty) const {
return nullptr;
}

Init *ListInit::convertInitListSlice(ArrayRef<unsigned> Elements) const {
if (Elements.size() == 1) {
if (Elements[0] >= size())
return nullptr;
return getElement(Elements[0]);
}

SmallVector<Init*, 8> Vals;
Vals.reserve(Elements.size());
for (unsigned Element : Elements) {
if (Element >= size())
return nullptr;
Vals.push_back(getElement(Element));
}
return ListInit::get(Vals, getElementType());
}

Record *ListInit::getElementAsRecord(unsigned i) const {
assert(i < NumValues && "List element index out of range!");
DefInit *DI = dyn_cast<DefInit>(getElement(i));
Expand Down Expand Up @@ -1200,7 +1181,36 @@ std::optional<bool> BinOpInit::CompareInit(unsigned Opc, Init *LHS, Init *RHS) c
}
break;
}
case RANGE: {
case LISTELEM: {
auto *TheList = dyn_cast<ListInit>(LHS);
auto *Idx = dyn_cast<IntInit>(RHS);
if (!TheList || !Idx)
break;
auto i = Idx->getValue();
if (i < 0 || i >= (ssize_t)TheList->size())
break;
return TheList->getElement(i);
}
case LISTSLICE: {
auto *TheList = dyn_cast<ListInit>(LHS);
auto *SliceIdxs = dyn_cast<ListInit>(RHS);
if (!TheList || !SliceIdxs)
break;
SmallVector<Init *, 8> Args;
Args.reserve(SliceIdxs->size());
for (auto *I : *SliceIdxs) {
auto *II = dyn_cast<IntInit>(I);
if (!II)
goto unresolved;
auto i = II->getValue();
if (i < 0 || i >= (ssize_t)TheList->size())
goto unresolved;
Args.push_back(TheList->getElement(i));
}
return ListInit::get(Args, TheList->getElementType());
}
case RANGE:
case RANGEC: {
auto *LHSi = dyn_cast<IntInit>(LHS);
auto *RHSi = dyn_cast<IntInit>(RHS);
if (!LHSi || !RHSi)
Expand All @@ -1209,7 +1219,20 @@ std::optional<bool> BinOpInit::CompareInit(unsigned Opc, Init *LHS, Init *RHS) c
auto Start = LHSi->getValue();
auto End = RHSi->getValue();
SmallVector<Init *, 8> Args;
if (Start < End) {
if (getOpcode() == RANGEC) {
// Closed interval
if (Start <= End) {
// Ascending order
Args.reserve(End - Start + 1);
for (auto i = Start; i <= End; ++i)
Args.push_back(IntInit::get(getRecordKeeper(), i));
} else {
// Descending order
Args.reserve(Start - End + 1);
for (auto i = Start; i >= End; --i)
Args.push_back(IntInit::get(getRecordKeeper(), i));
}
} else if (Start < End) {
// Half-open interval (excludes `End`)
Args.reserve(End - Start);
for (auto i = Start; i < End; ++i)
Expand Down Expand Up @@ -1308,6 +1331,7 @@ std::optional<bool> BinOpInit::CompareInit(unsigned Opc, Init *LHS, Init *RHS) c
break;
}
}
unresolved:
return const_cast<BinOpInit *>(this);
}

Expand All @@ -1324,6 +1348,11 @@ Init *BinOpInit::resolveReferences(Resolver &R) const {
std::string BinOpInit::getAsString() const {
std::string Result;
switch (getOpcode()) {
case LISTELEM:
case LISTSLICE:
return LHS->getAsString() + "[" + RHS->getAsString() + "]";
case RANGEC:
return LHS->getAsString() + "..." + RHS->getAsString();
case CONCAT: Result = "!con"; break;
case ADD: Result = "!add"; break;
case SUB: Result = "!sub"; break;
Expand Down Expand Up @@ -1908,22 +1937,6 @@ Init *TypedInit::getCastTo(RecTy *Ty) const {
->Fold(nullptr);
}

Init *TypedInit::convertInitListSlice(ArrayRef<unsigned> Elements) const {
ListRecTy *T = dyn_cast<ListRecTy>(getType());
if (!T) return nullptr; // Cannot subscript a non-list variable.

if (Elements.size() == 1)
return VarListElementInit::get(const_cast<TypedInit *>(this), Elements[0]);

SmallVector<Init*, 8> ListInits;
ListInits.reserve(Elements.size());
for (unsigned Element : Elements)
ListInits.push_back(VarListElementInit::get(const_cast<TypedInit *>(this),
Element));
return ListInit::get(ListInits, T->getElementType());
}


VarInit *VarInit::get(StringRef VN, RecTy *T) {
Init *Value = StringInit::get(T->getRecordKeeper(), VN);
return VarInit::get(Value, T);
Expand Down Expand Up @@ -1974,37 +1987,6 @@ Init *VarBitInit::resolveReferences(Resolver &R) const {
return const_cast<VarBitInit*>(this);
}

VarListElementInit *VarListElementInit::get(TypedInit *T, unsigned E) {
detail::RecordKeeperImpl &RK = T->getRecordKeeper().getImpl();
VarListElementInit *&I = RK.TheVarListElementInitPool[std::make_pair(T, E)];
if (!I)
I = new (RK.Allocator) VarListElementInit(T, E);
return I;
}

std::string VarListElementInit::getAsString() const {
return TI->getAsString() + "[" + utostr(Element) + "]";
}

Init *VarListElementInit::resolveReferences(Resolver &R) const {
Init *NewTI = TI->resolveReferences(R);
if (ListInit *List = dyn_cast<ListInit>(NewTI)) {
// Leave out-of-bounds array references as-is. This can happen without
// being an error, e.g. in the untaken "branch" of an !if expression.
if (getElementNum() < List->size())
return List->getElement(getElementNum());
}
if (NewTI != TI && isa<TypedInit>(NewTI))
return VarListElementInit::get(cast<TypedInit>(NewTI), getElementNum());
return const_cast<VarListElementInit *>(this);
}

Init *VarListElementInit::getBit(unsigned Bit) const {
if (getType() == BitRecTy::get(getRecordKeeper()))
return const_cast<VarListElementInit*>(this);
return VarBitInit::get(const_cast<VarListElementInit*>(this), Bit);
}

DefInit::DefInit(Record *D)
: TypedInit(IK_DefInit, D->getType()), Def(D) {}

Expand Down
178 changes: 169 additions & 9 deletions llvm/lib/TableGen/TGParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,148 @@ ParseSubMultiClassReference(MultiClass *CurMC) {
return Result;
}

/// ParseSliceElement - Parse subscript or range
///
/// SliceElement ::= Value<list<int>>
/// SliceElement ::= Value<int>
/// SliceElement ::= Value<int> '...' Value<int>
/// SliceElement ::= Value<int> '-' Value<int> (deprecated)
/// SliceElement ::= Value<int> INTVAL(Negative; deprecated)
///
/// SliceElement is either IntRecTy, ListRecTy, or nullptr
///
TypedInit *TGParser::ParseSliceElement(Record *CurRec) {
auto LHSLoc = Lex.getLoc();
auto *CurVal = ParseValue(CurRec);
if (!CurVal)
return nullptr;
auto *LHS = cast<TypedInit>(CurVal);

TypedInit *RHS = nullptr;
switch (Lex.getCode()) {
case tgtok::dotdotdot:
case tgtok::minus: { // Deprecated
Lex.Lex(); // eat
auto RHSLoc = Lex.getLoc();
CurVal = ParseValue(CurRec);
if (!CurVal)
return nullptr;
RHS = cast<TypedInit>(CurVal);
if (!isa<IntRecTy>(RHS->getType())) {
Error(RHSLoc,
"expected int...int, got " + Twine(RHS->getType()->getAsString()));
return nullptr;
}
break;
}
case tgtok::IntVal: { // Deprecated "-num"
auto i = -Lex.getCurIntVal();
if (i < 0) {
TokError("invalid range, cannot be negative");
return nullptr;
}
RHS = IntInit::get(Records, i);
Lex.Lex(); // eat IntVal
break;
}
default: // Single value (IntRecTy or ListRecTy)
return LHS;
}

assert(RHS);
assert(isa<IntRecTy>(RHS->getType()));

// Closed-interval range <LHS:IntRecTy>...<RHS:IntRecTy>
if (!isa<IntRecTy>(LHS->getType())) {
Error(LHSLoc,
"expected int...int, got " + Twine(LHS->getType()->getAsString()));
return nullptr;
}

return cast<TypedInit>(BinOpInit::get(BinOpInit::RANGEC, LHS, RHS,
IntRecTy::get(Records)->getListTy())
->Fold(CurRec));
}

/// ParseSliceElements - Parse subscripts in square brackets.
///
/// SliceElements ::= ( SliceElement ',' )* SliceElement ','?
///
/// SliceElement is either IntRecTy, ListRecTy, or nullptr
///
/// Returns ListRecTy by defaut.
/// Returns IntRecTy if;
/// - Single=true
/// - SliceElements is Value<int> w/o trailing comma
///
TypedInit *TGParser::ParseSliceElements(Record *CurRec, bool Single) {
TypedInit *CurVal;
SmallVector<Init *, 2> Elems; // int
SmallVector<TypedInit *, 2> Slices; // list<int>

auto FlushElems = [&] {
if (!Elems.empty()) {
Slices.push_back(ListInit::get(Elems, IntRecTy::get(Records)));
Elems.clear();
}
};

do {
auto LHSLoc = Lex.getLoc();
CurVal = ParseSliceElement(CurRec);
if (!CurVal)
return nullptr;
auto *CurValTy = CurVal->getType();

if (auto *ListValTy = dyn_cast<ListRecTy>(CurValTy)) {
if (!isa<IntRecTy>(ListValTy->getElementType())) {
Error(LHSLoc,
"expected list<int>, got " + Twine(ListValTy->getAsString()));
return nullptr;
}

FlushElems();
Slices.push_back(CurVal);
Single = false;
CurVal = nullptr;
} else if (!isa<IntRecTy>(CurValTy)) {
Error(LHSLoc,
"unhandled type " + Twine(CurValTy->getAsString()) + " in range");
return nullptr;
}

if (Lex.getCode() != tgtok::comma)
break;

Lex.Lex(); // eat comma

// `[i,]` is not LISTELEM but LISTSLICE
Single = false;
if (CurVal)
Elems.push_back(CurVal);
CurVal = nullptr;
} while (Lex.getCode() != tgtok::r_square);

if (CurVal) {
// LISTELEM
if (Single)
return CurVal;

Elems.push_back(CurVal);
}

FlushElems();

// Concatenate lists in Slices
TypedInit *Result = nullptr;
for (auto *Slice : Slices) {
Result = (Result ? cast<TypedInit>(BinOpInit::getListConcat(Result, Slice))
: Slice);
}

return Result;
}

/// ParseRangePiece - Parse a bit/value range.
/// RangePiece ::= INTVAL
/// RangePiece ::= INTVAL '...' INTVAL
Expand Down Expand Up @@ -2593,10 +2735,11 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
///
/// Value ::= SimpleValue ValueSuffix*
/// ValueSuffix ::= '{' BitList '}'
/// ValueSuffix ::= '[' BitList ']'
/// ValueSuffix ::= '[' SliceElements ']'
/// ValueSuffix ::= '.' ID
///
Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) {
SMLoc LHSLoc = Lex.getLoc();
Init *Result = ParseSimpleValue(CurRec, ItemType, Mode);
if (!Result) return nullptr;

Expand Down Expand Up @@ -2631,18 +2774,35 @@ Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) {
break;
}
case tgtok::l_square: {
SMLoc SquareLoc = Lex.getLoc();
Lex.Lex(); // eat the '['
SmallVector<unsigned, 16> Ranges;
ParseRangeList(Ranges);
if (Ranges.empty()) return nullptr;
auto *LHS = dyn_cast<TypedInit>(Result);
if (!LHS) {
Error(LHSLoc, "Invalid value, list expected");
return nullptr;
}

Result = Result->convertInitListSlice(Ranges);
if (!Result) {
Error(SquareLoc, "Invalid range for list slice");
auto *LHSTy = dyn_cast<ListRecTy>(LHS->getType());
if (!LHSTy) {
Error(LHSLoc, "Type '" + Twine(LHS->getType()->getAsString()) +
"' is invalid, list expected");
return nullptr;
}

Lex.Lex(); // eat the '['
TypedInit *RHS = ParseSliceElements(CurRec, /*Single=*/true);
if (!RHS)
return nullptr;

if (isa<ListRecTy>(RHS->getType())) {
Result =
BinOpInit::get(BinOpInit::LISTSLICE, LHS, RHS, LHSTy)->Fold(CurRec);
} else {
Result = BinOpInit::get(BinOpInit::LISTELEM, LHS, RHS,
LHSTy->getElementType())
->Fold(CurRec);
}

assert(Result);

// Eat the ']'.
if (!consume(tgtok::r_square)) {
TokError("expected ']' at end of list slice");
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/TableGen/TGParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@ class TGParser {
Record *CurRec);
bool ParseOptionalRangeList(SmallVectorImpl<unsigned> &Ranges);
bool ParseOptionalBitList(SmallVectorImpl<unsigned> &Ranges);
TypedInit *ParseSliceElement(Record *CurRec);
TypedInit *ParseSliceElements(Record *CurRec, bool Single = false);
void ParseRangeList(SmallVectorImpl<unsigned> &Result);
bool ParseRangePiece(SmallVectorImpl<unsigned> &Ranges,
TypedInit *FirstItem = nullptr);
Expand Down
72 changes: 72 additions & 0 deletions llvm/test/TableGen/ListSlices-fail.td
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Each RUN line is scattered.

defvar list_int = [0, 1, 2, 3, 4, 5];
defvar list_str = ["foo", "bar"];
defvar str = "hoge";
defvar und = ?;

#ifdef ERR0
// RUN: not llvm-tblgen %s -DERR0 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR0
// ERR0: [[FILE]]:[[@LINE+1]]:26: error: Unknown or reserved token when parsing a value
defvar errs = list_str [ ] ;
#endif

#ifdef ERR1
// RUN: not llvm-tblgen %s -DERR1 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR1
// ERR1: [[FILE]]:[[@LINE+1]]:26: error: Unknown or reserved token when parsing a value
defvar errs = list_str [ , ] ;
#endif

#ifdef ERR2
// RUN: not llvm-tblgen %s -DERR2 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR2
// ERR2: [[FILE]]:[[@LINE+1]]:26: error: expected list<int>, got list<string>
defvar errs = list_str [ list_str ] ;
#endif

#ifdef ERR3
// RUN: not llvm-tblgen %s -DERR3 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR3
// ERR3: [[FILE]]:[[@LINE+1]]:26: error: expected int...int, got list<string>
defvar errs = list_str [ list_str ... 42 ] ;
#endif

#ifdef ERR4
// RUN: not llvm-tblgen %s -DERR4 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR4
// ERR4: [[FILE]]:[[@LINE+1]]:32: error: expected int...int, got list<string>
defvar errs = list_str [ 0 ... list_str ] ;
#endif

#ifdef ERR5
// RUN: not llvm-tblgen %s -DERR5 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR5
// ERR5: [[FILE]]:[[@LINE+1]]:26: error: unhandled type string in range
defvar errs = list_str [ str ] ;
#endif

#ifdef ERR6
// RUN: not llvm-tblgen %s -DERR6 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR6
// ERR6: [[FILE]]:[[@LINE+1]]:28: error: invalid range, cannot be negative
defvar errs = list_str [ 5 1 ] ;
#endif

#ifdef ERR7
// RUN: not llvm-tblgen %s -DERR7 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR7
// ERR7: [[FILE]]:[[@LINE+1]]:15: error: Type 'string' is invalid, list expected
defvar errs = str [ 0 ] ;
#endif

#ifdef ERR8
// RUN: not llvm-tblgen %s -DERR8 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR8
// ERR8: [[FILE]]:[[@LINE+1]]:26: error: Unknown or reserved token when parsing a value
defvar errs = list_int [ ... ] ;
#endif

#ifdef ERR9
// RUN: not llvm-tblgen %s -DERR9 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR9
// ERR9: [[FILE]]:[[@LINE+1]]:32: error: Unknown or reserved token when parsing a value
defvar errs = list_int [ 0 ... ] ;
#endif

#ifdef ERRA
// RUN: not llvm-tblgen %s -DERRA 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERRA
// ERRA: [[FILE]]:[[@LINE+1]]:15: error: Invalid value, list expected
defvar errs = und [ 0 ] ;
#endif
39 changes: 39 additions & 0 deletions llvm/test/TableGen/ListSlices.td
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,42 @@ def Rec10 {
int Zero = Class1<[?, ?, 2, 3, ?, 5, ?]>.Zero;
list<int> TwoFive = Class1<[?, ?, 2, 3, ?, 5, ?]>.TwoFive;
}

// Test list[list] and list[int]
// CHECK: def Rec11
def Rec11 {
list<int> s5 = Var1[0...4];

// list[expr]
// CHECK: list<int> rev = [4, 3, 2, 1, 0];
list<int> rev = !foreach(i, s5, Var1[!sub(4, i)]);

// Slice by list[foreach]
// CHECK: list<int> revf = [4, 3, 2, 1, 0];
list<int> revf = Var1[!foreach(i, s5, !sub(4, i))];

// Simple slice
// CHECK: list<int> rr = [0, 1, 2, 3, 4];
list<int> rr = rev[rev];

// Trailing comma is acceptable
// CHECK: list<int> rr_ = [0, 1, 2, 3, 4];
list<int> rr_ = rev[rev,];

// Concatenation in slice
// CHECK: list<int> rrr = [1, 2, 4, 3, 2, 1, 0, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 8];
list<int> empty = [];
list<int> rrr = Var1[1, 2, rev, 3...6, 7, empty, rr, 8];

// Recognized as slice by the trailing comma
// CHECK: list<list<int>> rl1 = {{\[}}[0], [1], [2], [3], [4]];
list<list<int>> rl1 = !foreach(i, rev, rev[i,]);

// Slice by pair<int,int>
// CHECK: list<list<int>> rll = {{\[}}[0, 4], [1, 3], [2, 2], [3, 1], [4, 0]];
list<list<int>> rll = !foreach(i, rev, rev[i, !sub(4, i)]);

// Slice by dynamic range
// CHECK: list<list<int>> rlr = {{\[}}[4, 3, 2, 1, 0], [3, 2, 1], [2], [1, 2, 3], [0, 1, 2, 3, 4]];
list<list<int>> rlr = !foreach(i, s5, rev[i...!sub(4, i)]);
}
18 changes: 10 additions & 8 deletions llvm/utils/TableGen/CodeGenDAGPatterns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1858,7 +1858,7 @@ static unsigned GetNumNodeResults(Record *Operator, CodeGenDAGPatterns &CDP) {
return 0; // All return nothing.

if (Operator->isSubClassOf("Intrinsic"))
return CDP.getIntrinsic(Operator).IS.RetVTs.size();
return CDP.getIntrinsic(Operator).IS.RetTys.size();

if (Operator->isSubClassOf("SDNode"))
return CDP.getSDNodeInfo(Operator).getNumResults();
Expand Down Expand Up @@ -2511,11 +2511,12 @@ bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) {
bool MadeChange = false;

// Apply the result type to the node.
unsigned NumRetVTs = Int->IS.RetVTs.size();
unsigned NumParamVTs = Int->IS.ParamVTs.size();
unsigned NumRetVTs = Int->IS.RetTys.size();
unsigned NumParamVTs = Int->IS.ParamTys.size();

for (unsigned i = 0, e = NumRetVTs; i != e; ++i)
MadeChange |= UpdateNodeType(i, Int->IS.RetVTs[i], TP);
MadeChange |= UpdateNodeType(
i, getValueType(Int->IS.RetTys[i]->getValueAsDef("VT")), TP);

if (getNumChildren() != NumParamVTs + 1) {
TP.error("Intrinsic '" + Int->Name + "' expects " + Twine(NumParamVTs) +
Expand All @@ -2529,9 +2530,10 @@ bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) {
for (unsigned i = 0, e = getNumChildren()-1; i != e; ++i) {
MadeChange |= getChild(i+1)->ApplyTypeConstraints(TP, NotRegisters);

MVT::SimpleValueType OpVT = Int->IS.ParamVTs[i];
assert(getChild(i+1)->getNumTypes() == 1 && "Unhandled case");
MadeChange |= getChild(i+1)->UpdateNodeType(0, OpVT, TP);
MVT::SimpleValueType OpVT =
getValueType(Int->IS.ParamTys[i]->getValueAsDef("VT"));
assert(getChild(i + 1)->getNumTypes() == 1 && "Unhandled case");
MadeChange |= getChild(i + 1)->UpdateNodeType(0, OpVT, TP);
}
return MadeChange;
}
Expand Down Expand Up @@ -2983,7 +2985,7 @@ TreePatternNodePtr TreePattern::ParseTreePattern(Init *TheInit,

// If this intrinsic returns void, it must have side-effects and thus a
// chain.
if (Int.IS.RetVTs.empty())
if (Int.IS.RetTys.empty())
Operator = getDAGPatterns().get_intrinsic_void_sdnode();
else if (!Int.ME.doesNotAccessMemory() || Int.hasSideEffects)
// Has side-effects, requires chain.
Expand Down
104 changes: 11 additions & 93 deletions llvm/utils/TableGen/CodeGenIntrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,6 @@
#include <algorithm>
#include <cassert>
using namespace llvm;
using namespace llvm::tmp;

/// getValueType - Return the MVT::SimpleValueType that the specified TableGen
/// record corresponds to.
MVT::SimpleValueType llvm::tmp::getValueType(Record *Rec) {
return (MVT::SimpleValueType)Rec->getValueAsInt("Value");
}

//===----------------------------------------------------------------------===//
// CodeGenIntrinsic Implementation
Expand Down Expand Up @@ -120,91 +113,16 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R,
TargetPrefix + ".'!");
}

ListInit *RetTypes = R->getValueAsListInit("RetTypes");
ListInit *ParamTypes = R->getValueAsListInit("ParamTypes");

// First collate a list of overloaded types.
std::vector<MVT::SimpleValueType> OverloadedVTs;
for (ListInit *TypeList : {RetTypes, ParamTypes}) {
for (unsigned i = 0, e = TypeList->size(); i != e; ++i) {
Record *TyEl = TypeList->getElementAsRecord(i);
assert(TyEl->isSubClassOf("LLVMType") && "Expected a type!");

if (TyEl->isSubClassOf("LLVMMatchType"))
continue;
if (auto *Types = R->getValue("Types")) {
auto *TypeList = cast<ListInit>(Types->getValue());
isOverloaded = R->getValueAsBit("isOverloaded");

MVT::SimpleValueType VT = getValueType(TyEl->getValueAsDef("VT"));
if (MVT(VT).isOverloaded()) {
OverloadedVTs.push_back(VT);
isOverloaded = true;
}
}
}

// Parse the list of return types.
ListInit *TypeList = RetTypes;
for (unsigned i = 0, e = TypeList->size(); i != e; ++i) {
Record *TyEl = TypeList->getElementAsRecord(i);
assert(TyEl->isSubClassOf("LLVMType") && "Expected a type!");
MVT::SimpleValueType VT;
if (TyEl->isSubClassOf("LLVMMatchType")) {
unsigned MatchTy = TyEl->getValueAsInt("Number");
assert(MatchTy < OverloadedVTs.size() && "Invalid matching number!");
VT = OverloadedVTs[MatchTy];
// It only makes sense to use the extended and truncated vector element
// variants with iAny types; otherwise, if the intrinsic is not
// overloaded, all the types can be specified directly.
assert(((!TyEl->isSubClassOf("LLVMExtendedType") &&
!TyEl->isSubClassOf("LLVMTruncatedType")) ||
VT == MVT::iAny || VT == MVT::vAny) &&
"Expected iAny or vAny type");
} else {
VT = getValueType(TyEl->getValueAsDef("VT"));
}

// Reject invalid types.
if (VT == MVT::isVoid)
PrintFatalError(DefLoc, "Intrinsic '" + DefName +
" has void in result type list!");

IS.RetVTs.push_back(VT);
IS.RetTypeDefs.push_back(TyEl);
}

// Parse the list of parameter types.
TypeList = ParamTypes;
for (unsigned i = 0, e = TypeList->size(); i != e; ++i) {
Record *TyEl = TypeList->getElementAsRecord(i);
assert(TyEl->isSubClassOf("LLVMType") && "Expected a type!");
MVT::SimpleValueType VT;
if (TyEl->isSubClassOf("LLVMMatchType")) {
unsigned MatchTy = TyEl->getValueAsInt("Number");
if (MatchTy >= OverloadedVTs.size()) {
PrintError(R->getLoc(), "Parameter #" + Twine(i) +
" has out of bounds matching "
"number " +
Twine(MatchTy));
PrintFatalError(DefLoc,
Twine("ParamTypes is ") + TypeList->getAsString());
}
VT = OverloadedVTs[MatchTy];
// It only makes sense to use the extended and truncated vector element
// variants with iAny types; otherwise, if the intrinsic is not
// overloaded, all the types can be specified directly.
assert(((!TyEl->isSubClassOf("LLVMExtendedType") &&
!TyEl->isSubClassOf("LLVMTruncatedType")) ||
VT == MVT::iAny || VT == MVT::vAny) &&
"Expected iAny or vAny type");
} else
VT = getValueType(TyEl->getValueAsDef("VT"));

// Reject invalid types.
if (VT == MVT::isVoid && i != e - 1 /*void at end means varargs*/)
PrintFatalError(DefLoc, "Intrinsic '" + DefName +
" has void in result type list!");
unsigned I = 0;
for (unsigned E = R->getValueAsListInit("RetTypes")->size(); I < E; ++I)
IS.RetTys.push_back(TypeList->getElementAsRecord(I));

IS.ParamVTs.push_back(VT);
IS.ParamTypeDefs.push_back(TyEl);
for (unsigned E = TypeList->size(); I < E; ++I)
IS.ParamTys.push_back(TypeList->getElementAsRecord(I));
}

// Parse the intrinsic properties.
Expand Down Expand Up @@ -321,10 +239,10 @@ void CodeGenIntrinsic::setProperty(Record *R) {
}

bool CodeGenIntrinsic::isParamAPointer(unsigned ParamIdx) const {
if (ParamIdx >= IS.ParamVTs.size())
if (ParamIdx >= IS.ParamTys.size())
return false;
MVT ParamType = MVT(IS.ParamVTs[ParamIdx]);
return ParamType == MVT::iPTR || ParamType == MVT::iPTRAny;
return (IS.ParamTys[ParamIdx]->isSubClassOf("LLVMQualPointerType") ||
IS.ParamTys[ParamIdx]->isSubClassOf("LLVMAnyPointerType"));
}

bool CodeGenIntrinsic::isParamImmArg(unsigned ParamIdx) const {
Expand Down
18 changes: 2 additions & 16 deletions llvm/utils/TableGen/CodeGenIntrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

#include "SDNodeProperties.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/MachineValueType.h"
#include "llvm/Support/ModRef.h"
#include <string>
#include <tuple>
Expand All @@ -25,13 +24,6 @@ namespace llvm {
class Record;
class RecordKeeper;

// FIXME: Sweep this in the near future.
namespace tmp {
/// getValueType - Return the MVT::SimpleValueType that the specified TableGen
/// record corresponds to.
MVT::SimpleValueType getValueType(Record *Rec);
} // namespace tmp

struct CodeGenIntrinsic {
Record *TheDef; // The actual record defining this intrinsic.
std::string Name; // The name of the LLVM function "llvm.bswap.i32"
Expand All @@ -50,19 +42,13 @@ struct CodeGenIntrinsic {
/// only populated when in the context of a target .td file. When building
/// Intrinsics.td, this isn't available, because we don't know the target
/// pointer size.
std::vector<MVT::SimpleValueType> RetVTs;

/// The records for each return type.
std::vector<Record *> RetTypeDefs;
std::vector<Record *> RetTys;

/// The MVT::SimpleValueType for each parameter type. Note that this list is
/// only populated when in the context of a target .td file. When building
/// Intrinsics.td, this isn't available, because we don't know the target
/// pointer size.
std::vector<MVT::SimpleValueType> ParamVTs;

/// The records for each parameter type.
std::vector<Record *> ParamTypeDefs;
std::vector<Record *> ParamTys;
};

IntrinsicSignature IS;
Expand Down
321 changes: 4 additions & 317 deletions llvm/utils/TableGen/IntrinsicEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include "llvm/ADT/Twine.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MachineValueType.h"
#include "llvm/Support/ModRef.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TableGen/Error.h"
Expand All @@ -36,7 +35,6 @@
#include <utility>
#include <vector>
using namespace llvm;
using namespace llvm::tmp;

cl::OptionCategory GenIntrinsicCat("Options for -gen-intrinsic-enums");
cl::opt<std::string>
Expand Down Expand Up @@ -252,327 +250,16 @@ void IntrinsicEmitter::EmitIntrinsicToOverloadTable(
OS << "#endif\n\n";
}


// NOTE: This must be kept in synch with the copy in lib/IR/Function.cpp!
enum IIT_Info {
// Common values should be encoded with 0-15.
IIT_Done = 0,
IIT_I1 = 1,
IIT_I8 = 2,
IIT_I16 = 3,
IIT_I32 = 4,
IIT_I64 = 5,
IIT_F16 = 6,
IIT_F32 = 7,
IIT_F64 = 8,
IIT_V2 = 9,
IIT_V4 = 10,
IIT_V8 = 11,
IIT_V16 = 12,
IIT_V32 = 13,
IIT_PTR = 14,
IIT_ARG = 15,

// Values from 16+ are only encodable with the inefficient encoding.
IIT_V64 = 16,
IIT_MMX = 17,
IIT_TOKEN = 18,
IIT_METADATA = 19,
IIT_EMPTYSTRUCT = 20,
IIT_STRUCT2 = 21,
IIT_STRUCT3 = 22,
IIT_STRUCT4 = 23,
IIT_STRUCT5 = 24,
IIT_EXTEND_ARG = 25,
IIT_TRUNC_ARG = 26,
IIT_ANYPTR = 27,
IIT_V1 = 28,
IIT_VARARG = 29,
IIT_HALF_VEC_ARG = 30,
IIT_SAME_VEC_WIDTH_ARG = 31,
IIT_PTR_TO_ARG = 32,
IIT_PTR_TO_ELT = 33,
IIT_VEC_OF_ANYPTRS_TO_ELT = 34,
IIT_I128 = 35,
IIT_V512 = 36,
IIT_V1024 = 37,
IIT_STRUCT6 = 38,
IIT_STRUCT7 = 39,
IIT_STRUCT8 = 40,
IIT_F128 = 41,
IIT_VEC_ELEMENT = 42,
IIT_SCALABLE_VEC = 43,
IIT_SUBDIVIDE2_ARG = 44,
IIT_SUBDIVIDE4_ARG = 45,
IIT_VEC_OF_BITCASTS_TO_INT = 46,
IIT_V128 = 47,
IIT_BF16 = 48,
IIT_STRUCT9 = 49,
IIT_V256 = 50,
IIT_AMX = 51,
IIT_PPCF128 = 52,
IIT_V3 = 53,
IIT_EXTERNREF = 54,
IIT_FUNCREF = 55,
IIT_ANYPTR_TO_ELT = 56,
IIT_I2 = 57,
IIT_I4 = 58,
};

static void EncodeFixedValueType(MVT::SimpleValueType VT,
std::vector<unsigned char> &Sig) {
// clang-format off
if (MVT(VT).isInteger()) {
unsigned BitWidth = MVT(VT).getFixedSizeInBits();
switch (BitWidth) {
default: PrintFatalError("unhandled integer type width in intrinsic!");
case 1: return Sig.push_back(IIT_I1);
case 2: return Sig.push_back(IIT_I2);
case 4: return Sig.push_back(IIT_I4);
case 8: return Sig.push_back(IIT_I8);
case 16: return Sig.push_back(IIT_I16);
case 32: return Sig.push_back(IIT_I32);
case 64: return Sig.push_back(IIT_I64);
case 128: return Sig.push_back(IIT_I128);
}
}

switch (VT) {
default: PrintFatalError("unhandled MVT in intrinsic!");
case MVT::f16: return Sig.push_back(IIT_F16);
case MVT::bf16: return Sig.push_back(IIT_BF16);
case MVT::f32: return Sig.push_back(IIT_F32);
case MVT::f64: return Sig.push_back(IIT_F64);
case MVT::f128: return Sig.push_back(IIT_F128);
case MVT::ppcf128: return Sig.push_back(IIT_PPCF128);
case MVT::token: return Sig.push_back(IIT_TOKEN);
case MVT::Metadata: return Sig.push_back(IIT_METADATA);
case MVT::x86mmx: return Sig.push_back(IIT_MMX);
case MVT::x86amx: return Sig.push_back(IIT_AMX);
// MVT::OtherVT is used to mean the empty struct type here.
case MVT::Other: return Sig.push_back(IIT_EMPTYSTRUCT);
// MVT::isVoid is used to represent varargs here.
case MVT::isVoid: return Sig.push_back(IIT_VARARG);
case MVT::externref:
return Sig.push_back(IIT_EXTERNREF);
case MVT::funcref:
return Sig.push_back(IIT_FUNCREF);
}
// clang-format on
}

#if defined(_MSC_VER) && !defined(__clang__)
#pragma optimize("",off) // MSVC 2015 optimizer can't deal with this function.
#endif

static void EncodeFixedType(Record *R, std::vector<unsigned char> &ArgCodes,
unsigned &NextArgCode,
std::vector<unsigned char> &Sig,
ArrayRef<unsigned char> Mapping) {

if (R->isSubClassOf("LLVMMatchType")) {
unsigned Number = Mapping[R->getValueAsInt("Number")];
assert(Number < ArgCodes.size() && "Invalid matching number!");
if (R->isSubClassOf("LLVMExtendedType"))
Sig.push_back(IIT_EXTEND_ARG);
else if (R->isSubClassOf("LLVMTruncatedType"))
Sig.push_back(IIT_TRUNC_ARG);
else if (R->isSubClassOf("LLVMHalfElementsVectorType"))
Sig.push_back(IIT_HALF_VEC_ARG);
else if (R->isSubClassOf("LLVMScalarOrSameVectorWidth")) {
Sig.push_back(IIT_SAME_VEC_WIDTH_ARG);
Sig.push_back((Number << 3) | ArgCodes[Number]);
MVT::SimpleValueType VT = getValueType(R->getValueAsDef("ElTy"));
EncodeFixedValueType(VT, Sig);
return;
}
else if (R->isSubClassOf("LLVMPointerTo"))
Sig.push_back(IIT_PTR_TO_ARG);
else if (R->isSubClassOf("LLVMVectorOfAnyPointersToElt")) {
Sig.push_back(IIT_VEC_OF_ANYPTRS_TO_ELT);
// Encode overloaded ArgNo
Sig.push_back(NextArgCode++);
// Encode LLVMMatchType<Number> ArgNo
Sig.push_back(Number);
return;
} else if (R->isSubClassOf("LLVMAnyPointerToElt")) {
Sig.push_back(IIT_ANYPTR_TO_ELT);
// Encode overloaded ArgNo
Sig.push_back(NextArgCode++);
// Encode LLVMMatchType<Number> ArgNo
Sig.push_back(Number);
return;
} else if (R->isSubClassOf("LLVMPointerToElt"))
Sig.push_back(IIT_PTR_TO_ELT);
else if (R->isSubClassOf("LLVMVectorElementType"))
Sig.push_back(IIT_VEC_ELEMENT);
else if (R->isSubClassOf("LLVMSubdivide2VectorType"))
Sig.push_back(IIT_SUBDIVIDE2_ARG);
else if (R->isSubClassOf("LLVMSubdivide4VectorType"))
Sig.push_back(IIT_SUBDIVIDE4_ARG);
else if (R->isSubClassOf("LLVMVectorOfBitcastsToInt"))
Sig.push_back(IIT_VEC_OF_BITCASTS_TO_INT);
else
Sig.push_back(IIT_ARG);
return Sig.push_back((Number << 3) | 7 /*IITDescriptor::AK_MatchType*/);
}

MVT::SimpleValueType VT = getValueType(R->getValueAsDef("VT"));

unsigned Tmp = 0;
switch (VT) {
default: break;
case MVT::iPTRAny: ++Tmp; [[fallthrough]];
case MVT::vAny: ++Tmp; [[fallthrough]];
case MVT::fAny: ++Tmp; [[fallthrough]];
case MVT::iAny: ++Tmp; [[fallthrough]];
case MVT::Any: {
// If this is an "any" valuetype, then the type is the type of the next
// type in the list specified to getIntrinsic().
Sig.push_back(IIT_ARG);

// Figure out what arg # this is consuming, and remember what kind it was.
assert(NextArgCode < ArgCodes.size() && ArgCodes[NextArgCode] == Tmp &&
"Invalid or no ArgCode associated with overloaded VT!");
unsigned ArgNo = NextArgCode++;

// Encode what sort of argument it must be in the low 3 bits of the ArgNo.
return Sig.push_back((ArgNo << 3) | Tmp);
}

case MVT::iPTR: {
unsigned AddrSpace = 0;
if (R->isSubClassOf("LLVMQualPointerType")) {
AddrSpace = R->getValueAsInt("AddrSpace");
assert(AddrSpace < 256 && "Address space exceeds 255");
}
if (AddrSpace) {
Sig.push_back(IIT_ANYPTR);
Sig.push_back(AddrSpace);
} else {
Sig.push_back(IIT_PTR);
}
return EncodeFixedType(R->getValueAsDef("ElTy"), ArgCodes, NextArgCode, Sig,
Mapping);
}
}

if (MVT(VT).isVector()) {
MVT VVT = VT;
if (VVT.isScalableVector())
Sig.push_back(IIT_SCALABLE_VEC);
switch (VVT.getVectorMinNumElements()) {
default: PrintFatalError("unhandled vector type width in intrinsic!");
case 1: Sig.push_back(IIT_V1); break;
case 2: Sig.push_back(IIT_V2); break;
case 3: Sig.push_back(IIT_V3); break;
case 4: Sig.push_back(IIT_V4); break;
case 8: Sig.push_back(IIT_V8); break;
case 16: Sig.push_back(IIT_V16); break;
case 32: Sig.push_back(IIT_V32); break;
case 64: Sig.push_back(IIT_V64); break;
case 128: Sig.push_back(IIT_V128); break;
case 256: Sig.push_back(IIT_V256); break;
case 512: Sig.push_back(IIT_V512); break;
case 1024: Sig.push_back(IIT_V1024); break;
}

return EncodeFixedValueType(VVT.getVectorElementType().SimpleTy, Sig);
}

EncodeFixedValueType(VT, Sig);
}

static void UpdateArgCodes(Record *R, std::vector<unsigned char> &ArgCodes,
unsigned int &NumInserted,
SmallVectorImpl<unsigned char> &Mapping) {
if (R->isSubClassOf("LLVMMatchType")) {
if (R->isSubClassOf("LLVMVectorOfAnyPointersToElt")) {
ArgCodes.push_back(3 /*vAny*/);
++NumInserted;
} else if (R->isSubClassOf("LLVMAnyPointerToElt")) {
ArgCodes.push_back(4 /*iPTRAny*/);
++NumInserted;
}
return;
}

unsigned Tmp = 0;
switch (getValueType(R->getValueAsDef("VT"))) {
default: break;
case MVT::iPTR:
UpdateArgCodes(R->getValueAsDef("ElTy"), ArgCodes, NumInserted, Mapping);
break;
case MVT::iPTRAny:
++Tmp;
[[fallthrough]];
case MVT::vAny:
++Tmp;
[[fallthrough]];
case MVT::fAny:
++Tmp;
[[fallthrough]];
case MVT::iAny:
++Tmp;
[[fallthrough]];
case MVT::Any:
unsigned OriginalIdx = ArgCodes.size() - NumInserted;
assert(OriginalIdx >= Mapping.size());
Mapping.resize(OriginalIdx+1);
Mapping[OriginalIdx] = ArgCodes.size();
ArgCodes.push_back(Tmp);
break;
}
}

#if defined(_MSC_VER) && !defined(__clang__)
#pragma optimize("",on)
#endif

/// ComputeFixedEncoding - If we can encode the type signature for this
/// intrinsic into 32 bits, return it. If not, return ~0U.
static void ComputeFixedEncoding(const CodeGenIntrinsic &Int,
std::vector<unsigned char> &TypeSig) {
std::vector<unsigned char> ArgCodes;

// Add codes for any overloaded result VTs.
unsigned int NumInserted = 0;
SmallVector<unsigned char, 8> ArgMapping;
for (unsigned i = 0, e = Int.IS.RetVTs.size(); i != e; ++i)
UpdateArgCodes(Int.IS.RetTypeDefs[i], ArgCodes, NumInserted, ArgMapping);

// Add codes for any overloaded operand VTs.
for (unsigned i = 0, e = Int.IS.ParamTypeDefs.size(); i != e; ++i)
UpdateArgCodes(Int.IS.ParamTypeDefs[i], ArgCodes, NumInserted, ArgMapping);

unsigned NextArgCode = 0;
if (Int.IS.RetVTs.empty())
TypeSig.push_back(IIT_Done);
else if (Int.IS.RetVTs.size() == 1 &&
Int.IS.RetVTs[0] == MVT::isVoid)
TypeSig.push_back(IIT_Done);
else {
switch (Int.IS.RetVTs.size()) {
case 1: break;
case 2: TypeSig.push_back(IIT_STRUCT2); break;
case 3: TypeSig.push_back(IIT_STRUCT3); break;
case 4: TypeSig.push_back(IIT_STRUCT4); break;
case 5: TypeSig.push_back(IIT_STRUCT5); break;
case 6: TypeSig.push_back(IIT_STRUCT6); break;
case 7: TypeSig.push_back(IIT_STRUCT7); break;
case 8: TypeSig.push_back(IIT_STRUCT8); break;
case 9: TypeSig.push_back(IIT_STRUCT9); break;
default: llvm_unreachable("Unhandled case in struct");
if (auto *R = Int.TheDef->getValue("TypeSig")) {
for (auto &a : cast<ListInit>(R->getValue())->getValues()) {
for (auto &b : cast<ListInit>(a)->getValues())
TypeSig.push_back(cast<IntInit>(b)->getValue());
}

for (unsigned i = 0, e = Int.IS.RetVTs.size(); i != e; ++i)
EncodeFixedType(Int.IS.RetTypeDefs[i], ArgCodes, NextArgCode, TypeSig,
ArgMapping);
}

for (unsigned i = 0, e = Int.IS.ParamTypeDefs.size(); i != e; ++i)
EncodeFixedType(Int.IS.ParamTypeDefs[i], ArgCodes, NextArgCode, TypeSig,
ArgMapping);
}

static void printIITEntry(raw_ostream &OS, unsigned char X) {
Expand Down