TableGen: add !isa operation

Change-Id: Iddb724c3ae706d82933a2d82c91d07e0e36b30e3

Differential revision: https://reviews.llvm.org/D44105

llvm-svn: 327117
This commit is contained in:
Nicolai Haehnle 2018-03-09 12:24:06 +00:00
parent 47c3472c41
commit b537605956
8 changed files with 169 additions and 1 deletions

View File

@ -208,6 +208,9 @@ supported include:
is a special case in that the argument can be an int or a record. In the is a special case in that the argument can be an int or a record. In the
latter case, the record's name is returned. latter case, the record's name is returned.
``!isa<type>(a)``
Returns an integer: 1 if 'a' is dynamically of the given type, 0 otherwise.
``!subst(a, b, c)`` ``!subst(a, b, c)``
If 'a' and 'b' are of string type or are symbol references, substitute 'b' If 'a' and 'b' are of string type or are symbol references, substitute 'b'
for 'a' in 'c.' This operation is analogous to $(subst) in GNU make. for 'a' in 'c.' This operation is analogous to $(subst) in GNU make.

View File

@ -99,6 +99,7 @@ wide variety of meanings:
:!add !shl !sra !srl !and :!add !shl !sra !srl !and
:!or !empty !subst !foreach !strconcat :!or !empty !subst !foreach !strconcat
:!cast !listconcat !size !foldl :!cast !listconcat !size !foldl
:!isa
Syntax Syntax

View File

@ -317,6 +317,7 @@ protected:
IK_UnOpInit, IK_UnOpInit,
IK_LastOpInit, IK_LastOpInit,
IK_FoldOpInit, IK_FoldOpInit,
IK_IsAOpInit,
IK_StringInit, IK_StringInit,
IK_VarInit, IK_VarInit,
IK_VarListElementInit, IK_VarListElementInit,
@ -951,6 +952,39 @@ public:
std::string getAsString() const override; std::string getAsString() const override;
}; };
/// !isa<type>(expr) - Dynamically determine the type of an expression.
class IsAOpInit : public TypedInit, public FoldingSetNode {
private:
RecTy *CheckType;
Init *Expr;
IsAOpInit(RecTy *CheckType, Init *Expr)
: TypedInit(IK_IsAOpInit, IntRecTy::get()), CheckType(CheckType),
Expr(Expr) {}
public:
IsAOpInit(const IsAOpInit &) = delete;
IsAOpInit &operator=(const IsAOpInit &) = delete;
static bool classof(const Init *I) { return I->getKind() == IK_IsAOpInit; }
static IsAOpInit *get(RecTy *CheckType, Init *Expr);
void Profile(FoldingSetNodeID &ID) const;
// Fold - If possible, fold this to a simpler init. Return this if not
// possible to fold.
Init *Fold() const;
bool isComplete() const override { return false; }
Init *resolveReferences(Resolver &R) const override;
Init *getBit(unsigned Bit) const override;
std::string getAsString() const override;
};
/// 'Opcode' - Represent a reference to an entire variable object. /// 'Opcode' - Represent a reference to an entire variable object.
class VarInit : public TypedInit { class VarInit : public TypedInit {
Init *VarName; Init *VarName;

View File

@ -1231,6 +1231,68 @@ std::string FoldOpInit::getAsString() const {
.str(); .str();
} }
static void ProfileIsAOpInit(FoldingSetNodeID &ID, RecTy *CheckType,
Init *Expr) {
ID.AddPointer(CheckType);
ID.AddPointer(Expr);
}
IsAOpInit *IsAOpInit::get(RecTy *CheckType, Init *Expr) {
static FoldingSet<IsAOpInit> ThePool;
FoldingSetNodeID ID;
ProfileIsAOpInit(ID, CheckType, Expr);
void *IP = nullptr;
if (IsAOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP))
return I;
IsAOpInit *I = new (Allocator) IsAOpInit(CheckType, Expr);
ThePool.InsertNode(I, IP);
return I;
}
void IsAOpInit::Profile(FoldingSetNodeID &ID) const {
ProfileIsAOpInit(ID, CheckType, Expr);
}
Init *IsAOpInit::Fold() const {
if (TypedInit *TI = dyn_cast<TypedInit>(Expr)) {
// Is the expression type known to be (a subclass of) the desired type?
if (TI->getType()->typeIsConvertibleTo(CheckType))
return IntInit::get(1);
if (isa<RecordRecTy>(CheckType)) {
// If the target type is not a subclass of the expression type, or if
// the expression has fully resolved to a record, we know that it can't
// be of the required type.
if (!CheckType->typeIsConvertibleTo(TI->getType()) || isa<DefInit>(Expr))
return IntInit::get(0);
} else {
// We treat non-record types as not castable.
return IntInit::get(0);
}
}
return const_cast<IsAOpInit *>(this);
}
Init *IsAOpInit::resolveReferences(Resolver &R) const {
Init *NewExpr = Expr->resolveReferences(R);
if (Expr != NewExpr)
return get(CheckType, NewExpr)->Fold();
return const_cast<IsAOpInit *>(this);
}
Init *IsAOpInit::getBit(unsigned Bit) const {
return VarBitInit::get(const_cast<IsAOpInit *>(this), Bit);
}
std::string IsAOpInit::getAsString() const {
return (Twine("!isa<") + CheckType->getAsString() + ">(" +
Expr->getAsString() + ")")
.str();
}
RecTy *TypedInit::getFieldType(StringInit *FieldName) const { RecTy *TypedInit::getFieldType(StringInit *FieldName) const {
if (RecordRecTy *RecordType = dyn_cast<RecordRecTy>(getType())) { if (RecordRecTy *RecordType = dyn_cast<RecordRecTy>(getType())) {
for (Record *Rec : RecordType->getClasses()) { for (Record *Rec : RecordType->getClasses()) {

View File

@ -467,6 +467,7 @@ tgtok::TokKind TGLexer::LexExclaim() {
StringSwitch<tgtok::TokKind>(StringRef(Start, CurPtr - Start)) StringSwitch<tgtok::TokKind>(StringRef(Start, CurPtr - Start))
.Case("eq", tgtok::XEq) .Case("eq", tgtok::XEq)
.Case("if", tgtok::XIf) .Case("if", tgtok::XIf)
.Case("isa", tgtok::XIsA)
.Case("head", tgtok::XHead) .Case("head", tgtok::XHead)
.Case("tail", tgtok::XTail) .Case("tail", tgtok::XTail)
.Case("size", tgtok::XSize) .Case("size", tgtok::XSize)

View File

@ -48,7 +48,7 @@ namespace tgtok {
// !keywords. // !keywords.
XConcat, XADD, XAND, XOR, XSRA, XSRL, XSHL, XListConcat, XStrConcat, XCast, XConcat, XADD, XAND, XOR, XSRA, XSRL, XSHL, XListConcat, XStrConcat, XCast,
XSubst, XForEach, XFoldl, XHead, XTail, XSize, XEmpty, XIf, XEq, XSubst, XForEach, XFoldl, XHead, XTail, XSize, XEmpty, XIf, XEq, XIsA,
// Integer value. // Integer value.
IntVal, IntVal,

View File

@ -936,6 +936,33 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
return (UnOpInit::get(Code, LHS, Type))->Fold(CurRec, CurMultiClass); return (UnOpInit::get(Code, LHS, Type))->Fold(CurRec, CurMultiClass);
} }
case tgtok::XIsA: {
// Value ::= !isa '<' Type '>' '(' Value ')'
Lex.Lex(); // eat the operation
RecTy *Type = ParseOperatorType();
if (!Type)
return nullptr;
if (Lex.getCode() != tgtok::l_paren) {
TokError("expected '(' after type of !isa");
return nullptr;
}
Lex.Lex(); // eat the '('
Init *LHS = ParseValue(CurRec);
if (!LHS)
return nullptr;
if (Lex.getCode() != tgtok::r_paren) {
TokError("expected ')' in !isa");
return nullptr;
}
Lex.Lex(); // eat the ')'
return (IsAOpInit::get(Type, LHS))->Fold();
}
case tgtok::XConcat: case tgtok::XConcat:
case tgtok::XADD: case tgtok::XADD:
case tgtok::XAND: case tgtok::XAND:
@ -1696,6 +1723,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
case tgtok::XSize: case tgtok::XSize:
case tgtok::XEmpty: case tgtok::XEmpty:
case tgtok::XCast: // Value ::= !unop '(' Value ')' case tgtok::XCast: // Value ::= !unop '(' Value ')'
case tgtok::XIsA:
case tgtok::XConcat: case tgtok::XConcat:
case tgtok::XADD: case tgtok::XADD:
case tgtok::XAND: case tgtok::XAND:

39
llvm/test/TableGen/isa.td Normal file
View File

@ -0,0 +1,39 @@
// RUN: llvm-tblgen %s | FileCheck %s
// XFAIL: vg_leak
// CHECK: --- Defs ---
// CHECK: def X0 {
// CHECK: int ret = 0;
// CHECK: }
// CHECK: def X1 {
// CHECK: int ret = 1;
// CHECK: }
// CHECK: def Y0 {
// CHECK: int ret = 0;
// CHECK: }
// CHECK: def Y1 {
// CHECK: int ret = 11;
// CHECK: }
class A<int dummy>;
class B<int num> : A<num> {
int Num = num;
}
class X<A a> {
int ret = !isa<B>(a);
}
class Y<A a> {
int ret = !if(!isa<B>(a), !cast<B>(a).Num, 0);
}
def X0 : X<A<0>>;
def X1 : X<B<0>>;
def Y0 : Y<A<10>>;
def Y1 : Y<B<11>>;