TableGen: add !isa operation
Change-Id: Iddb724c3ae706d82933a2d82c91d07e0e36b30e3 Differential revision: https://reviews.llvm.org/D44105 llvm-svn: 327117
This commit is contained in:
parent
47c3472c41
commit
b537605956
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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>>;
|
Loading…
Reference in New Issue