TableGen: Delay instantiating inline anonymous records

Summary:
Only instantiate anonymous records once all variable references in template
arguments have been resolved. This allows patterns like the new test case,
which in practice can appear in expressions like:

  class IntrinsicTypeProfile<list<LLVMType> ty, int shift> {
    list<LLVMType> types =
      !listconcat(ty, [llvm_any_ty, LLVMMatchType<shift>]);
  }

  class FooIntrinsic<IntrinsicTypeProfile P, ...>
    : Intrinsic<..., P.types, ...>;

Without this change, the anonymous LLVMMatchType instantiation would
never get resolved.

Another consequence of this change is that anonymous inline
instantiations are uniqued via the folding set of the newly introduced
VarDefInit.

Change-Id: I7a7041a20e297cf98c9109b28d85e64e176c932a

Reviewers: arsenm, craig.topper, tra, MartinO

Subscribers: wdng, llvm-commits

Differential Revision: https://reviews.llvm.org/D43756

llvm-svn: 326788
This commit is contained in:
Nicolai Haehnle 2018-03-06 13:49:01 +00:00
parent 73355bcd2a
commit d4c0a5d08d
4 changed files with 272 additions and 46 deletions

View File

@ -320,6 +320,7 @@ protected:
IK_VarInit,
IK_VarListElementInit,
IK_VarBitInit,
IK_VarDefInit,
IK_LastTypedInit,
IK_UnsetInit
};
@ -1052,6 +1053,58 @@ public:
}
};
/// classname<targs...> - Represent an uninstantiated anonymous class
/// instantiation.
class VarDefInit final : public TypedInit, public FoldingSetNode,
public TrailingObjects<VarDefInit, Init *> {
Record *Class;
DefInit *Def = nullptr; // after instantiation
unsigned NumArgs;
explicit VarDefInit(Record *Class, unsigned N)
: TypedInit(IK_VarDefInit, RecordRecTy::get(Class)), Class(Class), NumArgs(N) {}
DefInit *instantiate();
public:
VarDefInit(const VarDefInit &) = delete;
VarDefInit &operator=(const VarDefInit &) = delete;
// Do not use sized deallocation due to trailing objects.
void operator delete(void *p) { ::operator delete(p); }
static bool classof(const Init *I) {
return I->getKind() == IK_VarDefInit;
}
static VarDefInit *get(Record *Class, ArrayRef<Init *> Args);
void Profile(FoldingSetNodeID &ID) const;
Init *resolveReferences(Resolver &R) const override;
Init *Fold() const;
std::string getAsString() const override;
Init *getArg(unsigned i) const {
assert(i < NumArgs && "Argument index out of range!");
return getTrailingObjects<Init *>()[i];
}
using const_iterator = Init *const *;
const_iterator args_begin() const { return getTrailingObjects<Init *>(); }
const_iterator args_end () const { return args_begin() + NumArgs; }
size_t args_size () const { return NumArgs; }
bool args_empty() const { return NumArgs == 0; }
ArrayRef<Init *> args() const { return makeArrayRef(args_begin(), NumArgs); }
Init *getBit(unsigned Bit) const override {
llvm_unreachable("Illegal bit reference off anonymous def");
}
};
/// X.Y - Represent a reference to a subfield of a variable
class FieldInit : public TypedInit {
Init *Rec; // Record we are referring to
@ -1754,6 +1807,21 @@ public:
}
};
/// (Optionally) delegate resolving to a sub-resolver, and keep track whether
/// there were unresolved references.
class TrackUnresolvedResolver final : public Resolver {
Resolver *R;
bool FoundUnresolved = false;
public:
explicit TrackUnresolvedResolver(Resolver *R = nullptr)
: Resolver(R ? R->getCurrentRecord() : nullptr), R(R) {}
bool foundUnresolved() const { return FoundUnresolved; }
Init *resolve(Init *VarName) override;
};
} // end namespace llvm
#endif // LLVM_TABLEGEN_RECORD_H

View File

@ -1355,6 +1355,132 @@ std::string DefInit::getAsString() const {
return Def->getName();
}
static void ProfileVarDefInit(FoldingSetNodeID &ID,
Record *Class,
ArrayRef<Init *> Args) {
ID.AddInteger(Args.size());
ID.AddPointer(Class);
for (Init *I : Args)
ID.AddPointer(I);
}
VarDefInit *VarDefInit::get(Record *Class, ArrayRef<Init *> Args) {
static FoldingSet<VarDefInit> ThePool;
FoldingSetNodeID ID;
ProfileVarDefInit(ID, Class, Args);
void *IP = nullptr;
if (VarDefInit *I = ThePool.FindNodeOrInsertPos(ID, IP))
return I;
void *Mem = Allocator.Allocate(totalSizeToAlloc<Init *>(Args.size()),
alignof(VarDefInit));
VarDefInit *I = new(Mem) VarDefInit(Class, Args.size());
std::uninitialized_copy(Args.begin(), Args.end(),
I->getTrailingObjects<Init *>());
ThePool.InsertNode(I, IP);
return I;
}
void VarDefInit::Profile(FoldingSetNodeID &ID) const {
ProfileVarDefInit(ID, Class, args());
}
DefInit *VarDefInit::instantiate() {
if (!Def) {
RecordKeeper &Records = Class->getRecords();
auto NewRecOwner = make_unique<Record>(Records.getNewAnonymousName(),
Class->getLoc(), Records,
/*IsAnonymous=*/true);
Record *NewRec = NewRecOwner.get();
// Copy values from class to instance
for (const RecordVal &Val : Class->getValues()) {
if (Val.getName() != "NAME")
NewRec->addValue(Val);
}
// Substitute and resolve template arguments
ArrayRef<Init *> TArgs = Class->getTemplateArgs();
MapResolver R(NewRec);
for (unsigned i = 0, e = TArgs.size(); i != e; ++i) {
if (i < args_size())
R.set(TArgs[i], getArg(i));
else
R.set(TArgs[i], NewRec->getValue(TArgs[i])->getValue());
NewRec->removeValue(TArgs[i]);
}
NewRec->resolveReferences(R);
// Add superclasses.
ArrayRef<std::pair<Record *, SMRange>> SCs = Class->getSuperClasses();
for (const auto &SCPair : SCs)
NewRec->addSuperClass(SCPair.first, SCPair.second);
NewRec->addSuperClass(Class,
SMRange(Class->getLoc().back(),
Class->getLoc().back()));
// Resolve internal references and store in record keeper
NewRec->resolveReferences();
Records.addDef(std::move(NewRecOwner));
Def = DefInit::get(NewRec);
}
return Def;
}
Init *VarDefInit::resolveReferences(Resolver &R) const {
TrackUnresolvedResolver UR(&R);
bool Changed = false;
SmallVector<Init *, 8> NewArgs;
NewArgs.reserve(args_size());
for (Init *Arg : args()) {
Init *NewArg = Arg->resolveReferences(UR);
NewArgs.push_back(NewArg);
Changed |= NewArg != Arg;
}
if (Changed) {
auto New = VarDefInit::get(Class, NewArgs);
if (!UR.foundUnresolved())
return New->instantiate();
return New;
}
return const_cast<VarDefInit *>(this);
}
Init *VarDefInit::Fold() const {
if (Def)
return Def;
TrackUnresolvedResolver R;
for (Init *Arg : args())
Arg->resolveReferences(R);
if (!R.foundUnresolved())
return const_cast<VarDefInit *>(this)->instantiate();
return const_cast<VarDefInit *>(this);
}
std::string VarDefInit::getAsString() const {
std::string Result = Class->getNameInitAsString() + "<";
const char *sep = "";
for (Init *Arg : args()) {
Result += sep;
sep = ", ";
Result += Arg->getAsString();
}
return Result + ">";
}
FieldInit *FieldInit::get(Init *R, StringInit *FN) {
using Key = std::pair<Init *, StringInit *>;
static DenseMap<Key, FieldInit*> ThePool;
@ -1917,3 +2043,23 @@ Init *RecordResolver::resolve(Init *VarName) {
Cache[VarName] = Val;
return Val;
}
Init *TrackUnresolvedResolver::resolve(Init *VarName) {
Init *I = nullptr;
if (R) {
I = R->resolve(VarName);
if (I && !FoundUnresolved) {
// Do not recurse into the resolved initializer, as that would change
// the behavior of the resolver we're delegating, but do check to see
// if there are unresolved variables remaining.
TrackUnresolvedResolver Sub;
I->resolveReferences(Sub);
FoundUnresolved |= Sub.FoundUnresolved;
}
}
if (!I)
FoundUnresolved = true;
return I;
}

View File

@ -1346,61 +1346,49 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
return nullptr;
}
SubClassReference SCRef;
ParseValueList(SCRef.TemplateArgs, CurRec, Class);
if (SCRef.TemplateArgs.empty()) return nullptr;
SmallVector<Init *, 8> Args;
ParseValueList(Args, CurRec, Class);
if (Args.empty()) return nullptr;
if (Lex.getCode() != tgtok::greater) {
TokError("expected '>' at end of value list");
return nullptr;
}
Lex.Lex(); // eat the '>'
SMLoc EndLoc = Lex.getLoc();
// Create the new record, set it as CurRec temporarily.
auto NewRecOwner =
make_unique<Record>(Records.getNewAnonymousName(), NameLoc, Records,
/*IsAnonymous=*/true);
Record *NewRec = NewRecOwner.get(); // Keep a copy since we may release.
SCRef.RefRange = SMRange(NameLoc, EndLoc);
SCRef.Rec = Class;
// Add info about the subclass to NewRec.
if (AddSubClass(NewRec, SCRef))
// Typecheck the template arguments list
ArrayRef<Init *> ExpectedArgs = Class->getTemplateArgs();
if (ExpectedArgs.size() < Args.size()) {
Error(NameLoc,
"More template args specified than expected");
return nullptr;
if (!CurMultiClass) {
NewRec->resolveReferences();
Records.addDef(std::move(NewRecOwner));
} else {
// This needs to get resolved once the multiclass template arguments are
// known before any use.
NewRec->setResolveFirst(true);
// Otherwise, we're inside a multiclass, add it to the multiclass.
CurMultiClass->DefPrototypes.push_back(std::move(NewRecOwner));
// Copy the template arguments for the multiclass into the def.
for (Init *TArg : CurMultiClass->Rec.getTemplateArgs()) {
const RecordVal *RV = CurMultiClass->Rec.getValue(TArg);
assert(RV && "Template arg doesn't exist?");
NewRec->addValue(*RV);
}
// We can't return the prototype def here, instead return:
// !cast<ItemType>(!strconcat(NAME, AnonName)).
const RecordVal *MCNameRV = CurMultiClass->Rec.getValue("NAME");
assert(MCNameRV && "multiclass record must have a NAME");
return UnOpInit::get(UnOpInit::CAST,
BinOpInit::get(BinOpInit::STRCONCAT,
VarInit::get(MCNameRV->getName(),
MCNameRV->getType()),
NewRec->getNameInit(),
StringRecTy::get()),
NewRec->getDefInit()->getType());
}
// The result of the expression is a reference to the new record.
return DefInit::get(NewRec);
for (unsigned i = 0, e = ExpectedArgs.size(); i != e; ++i) {
RecordVal *ExpectedArg = Class->getValue(ExpectedArgs[i]);
if (i < Args.size()) {
if (TypedInit *TI = dyn_cast<TypedInit>(Args[i])) {
RecTy *ExpectedType = ExpectedArg->getType();
if (!TI->getType()->typeIsConvertibleTo(ExpectedType)) {
Error(NameLoc,
"Value specified for template argument #" + Twine(i) + " (" +
ExpectedArg->getNameInitAsString() + ") is of type '" +
TI->getType()->getAsString() + "', expected '" +
ExpectedType->getAsString() + "': " + TI->getAsString());
return nullptr;
}
continue;
}
} else if (ExpectedArg->getValue()->isComplete())
continue;
Error(NameLoc,
"Value not specified for template argument #" + Twine(i) + " (" +
ExpectedArgs[i]->getAsUnquotedString() + ")");
return nullptr;
}
return VarDefInit::get(Class, Args)->Fold();
}
case tgtok::l_brace: { // Value ::= '{' ValueList '}'
SMLoc BraceLoc = Lex.getLoc();

View File

@ -1,6 +1,24 @@
// RUN: llvm-tblgen < %s
// RUN: llvm-tblgen %s | FileCheck %s
// XFAIL: vg_leak
// CHECK: --- Defs ---
// CHECK: def X {
// CHECK: foo Y = anonymous_0;
// CHECK: }
// CHECK: def ZD {
// CHECK: foo Z = anonymous_1;
// CHECK: }
// CHECK: def anonymous_0 {
// CHECK: int THEVAL = 1;
// CHECK: }
// CHECK: def anonymous_1 {
// CHECK: int THEVAL = 42;
// CHECK: }
class foo<int X> { int THEVAL = X; }
def foo_imp : foo<1>;
@ -11,3 +29,9 @@ def x {
def X {
foo Y = foo<1>; // This should work too, synthesizing a new foo<1>.
}
class Z<int X> {
foo Z = foo<X>;
}
def ZD : Z<42>;