Index: llvm/include/llvm/TableGen/Record.h =================================================================== --- llvm/include/llvm/TableGen/Record.h +++ llvm/include/llvm/TableGen/Record.h @@ -1269,6 +1269,7 @@ unsigned ID; bool IsAnonymous; + bool IsMCRecord; // Record within multiclass. // Class-instance values can be used by other defs. For example, Struct // is used here as a template argument to another class: @@ -1288,9 +1289,9 @@ public: // Constructs a record. explicit Record(Init *N, ArrayRef locs, RecordKeeper &records, - bool Anonymous = false) : + bool Anonymous = false, bool MCRecord = false) : Name(N), Locs(locs.begin(), locs.end()), TrackedRecords(records), - ID(LastID++), IsAnonymous(Anonymous) { + ID(LastID++), IsAnonymous(Anonymous), IsMCRecord(MCRecord) { init(); } @@ -1300,12 +1301,15 @@ // When copy-constructing a Record, we must still guarantee a globally unique // ID number. Don't copy TheInit either since it's owned by the original - // record. All other fields can be copied normally. - Record(const Record &O) : - Name(O.Name), Locs(O.Locs), TemplateArgs(O.TemplateArgs), - Values(O.Values), SuperClasses(O.SuperClasses), - TrackedRecords(O.TrackedRecords), ID(LastID++), - IsAnonymous(O.IsAnonymous), ResolveFirst(O.ResolveFirst) { } + // record. IsMCRecord is reset as it should be set explicitly and we don't + // want to propagate it outside of a multiclass. All other fields can be + // copied normally. + Record(const Record &O) + : Name(O.Name), Locs(O.Locs), TemplateArgs(O.TemplateArgs), + Values(O.Values), SuperClasses(O.SuperClasses), + TrackedRecords(O.TrackedRecords), ID(LastID++), + IsAnonymous(O.IsAnonymous), IsMCRecord(false), + ResolveFirst(O.ResolveFirst) {} static unsigned getNewUID() { return LastID++; } @@ -1441,6 +1445,9 @@ ResolveFirst = b; } + bool isMCRecord() const { return IsMCRecord; } + void setMCRecord(bool value) { IsMCRecord = value; } + void print(raw_ostream &OS) const; void dump() const; Index: llvm/lib/TableGen/Record.cpp =================================================================== --- llvm/lib/TableGen/Record.cpp +++ llvm/lib/TableGen/Record.cpp @@ -675,6 +675,12 @@ if (Record *D = (CurRec->getRecords()).getDef(Name->getValue())) return DefInit::get(D); + // Failure to resolve a reference while we're parsing a multiclass is + // not fatal. We'll attempt the cast again when we instantiate the + // record for real. + if (CurRec->isMCRecord()) + break; + PrintFatalError(CurRec->getLoc(), "Undefined reference:'" + Name->getValue() + "'\n"); } Index: llvm/lib/TableGen/TGParser.cpp =================================================================== --- llvm/lib/TableGen/TGParser.cpp +++ llvm/lib/TableGen/TGParser.cpp @@ -2372,6 +2372,13 @@ Locs.append(DefProto->getLoc().begin(), DefProto->getLoc().end()); auto CurRec = make_unique(DefName, Locs, Records, IsAnonymous); + // Mark it as a record internal to a multiclass. These make some errors + // non-fatal. E.g. when the record uses !cast for a record that does not exist + // yet, because it may be instantiated only after we're done parsing the + // multiclass. + if (CurMultiClass != nullptr) + CurRec->setMCRecord(true); + SubClassReference Ref; Ref.RefRange = DefmPrefixRange; Ref.Rec = DefProto; Index: llvm/test/TableGen/MultiClass-defm-fail.td =================================================================== --- /dev/null +++ llvm/test/TableGen/MultiClass-defm-fail.td @@ -0,0 +1,32 @@ +// RUN: not llvm-tblgen %s 2>&1 | FileCheck %s +// XFAIL: vg_leak + +// This test verifies that tablegen does fail if it can't resolve an unresolved +// !cast() during processing top-level defm. + +class A {} +class B { + A ba = a; +} + +multiclass M0 { + // This should work fine. + def _m00 : B(s)>; + // CHECK: error: Undefined reference:'d1_r1_no_such_record' + def _m01: B(s#"_no_such_record")>; +} + +multiclass M1 { + def _r1 : A; + // It would be nice if we could refer to _r1's name without having to pass it + // explicitly via 's'. + // XCHECK-DAG: note: instantiated from multiclass + defm _m1: M0; +} + +// CHECK: defm d1: M1 +// CHECK: note: instantiated from multiclass +// CHECK: defm _m1: M0 +// CHECK: note: instantiated from multiclass +// CHECK: def _m01: B +defm d1: M1<"d1">; Index: llvm/test/TableGen/MultiClass-defm.td =================================================================== --- /dev/null +++ llvm/test/TableGen/MultiClass-defm.td @@ -0,0 +1,44 @@ +// RUN: llvm-tblgen %s | FileCheck %s +// XFAIL: vg_leak + +// Some of the things that would be an error in a top-level defm, should be OK +// within the multiclass. This test verifies that tablegen can handle !cast to a +// record that does not exist during multiclass parsing, but which does get +// created by the time the multiclass record is instantiated. + +class A {} +class B { + A ba = a; +} + +multiclass M0 { + def _m0 : B(s)>; + + // Uncomment to test that !cast will eventually fail if the record it refers + // to does not exist by the time we instantiate this record from the top + // level. + //def _m1 : B(s#"X")>; +} + +multiclass M1 { + def _r1 : A; + // It would be nice if we could refer to _r1's name without having to pass it + // explicitly via 's'. + defm _m1: M0; +} + +multiclass M2 { + // This used to throw an error during multiclass parsing as M1::_r1 is not + // instantiated yet when we attempt to instantiare _m1 within the multiclass. + defm NAME: M1<"d0">; +} +defm d0: M2; +// CHECK-LABEL: def d0_m1_m0 +// CHECK: A ba = d0_r1; + +// This always works, because d1_r1 is instantiated before d1_m1 which would +// attempt to !cast to it. +defm d1: M1<"d1">; +// CHECK-LABEL: def d1_m1_m0 +// CHECK: A ba = d1_r1; +