2003-10-06 03:27:59 +08:00
|
|
|
//===- Record.cpp - Record implementation ---------------------------------===//
|
2005-04-22 08:00:37 +08:00
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2005-04-22 08:00:37 +08:00
|
|
|
//
|
2003-10-21 04:20:30 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2003-10-06 03:27:59 +08:00
|
|
|
//
|
2006-04-01 05:53:49 +08:00
|
|
|
// Implement the tablegen record classes.
|
2003-10-06 03:27:59 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2017-06-16 08:43:26 +08:00
|
|
|
#include "llvm/ADT/ArrayRef.h"
|
2011-07-30 03:07:11 +08:00
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include "llvm/ADT/FoldingSet.h"
|
2016-12-04 13:48:06 +08:00
|
|
|
#include "llvm/ADT/SmallString.h"
|
2012-12-04 00:50:05 +08:00
|
|
|
#include "llvm/ADT/SmallVector.h"
|
[tblgen] Track CodeInit origins when possible
Summary:
Add an SMLoc to CodeInit that records the source line it originated from.
This allows tablegen to point precisely at portions of code when reporting
errors within the CodeInit. For example, in the upcoming GlobalISel
combiner, it can report undefined expansions and point at the instance of
the expansion. This is achieved using something like:
SMLoc::getFromPointer(SMLoc::getPointer() +
(StringRef - CodeInit::getValue()))
The location is lost when producing a CodeInit by string concatenation so
a fallback SMLoc is required (e.g. the Record::getLoc()) but that's pretty
rare for CodeInits.
There's a reasonable case for extending tracking of a couple other Init
objects, for example StringInit's are often parsed and it would be good to
point inside the string when reporting errors about that. However, location
tracking also harms de-duplication. This is fine for CodeInit where there's
only a few hundred of them (~160 for X86) and it may be worth it for
StringInit (~86k up to ~1.9M for roughly 15MB increase for X86).
However the origin tracking would be a _terrible_ idea for IntInit, BitInit,
and UnsetInit. I haven't measured either of those three but BitInit would
most likely be on the order of increasing the current 2 BitInit values up
to billions.
Reviewers: volkan, aditya_nandakumar, bogner, paquette, aemerson
Reviewed By: paquette
Subscribers: javed.absar, kristof.beyls, dexonsmith, llvm-commits, kristina
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D58141
llvm-svn: 355245
2019-03-02 08:12:57 +08:00
|
|
|
#include "llvm/ADT/Statistic.h"
|
2007-11-21 06:25:16 +08:00
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2011-07-30 03:07:14 +08:00
|
|
|
#include "llvm/ADT/StringMap.h"
|
2017-06-16 08:43:26 +08:00
|
|
|
#include "llvm/ADT/StringRef.h"
|
[tblgen] Track CodeInit origins when possible
Summary:
Add an SMLoc to CodeInit that records the source line it originated from.
This allows tablegen to point precisely at portions of code when reporting
errors within the CodeInit. For example, in the upcoming GlobalISel
combiner, it can report undefined expansions and point at the instance of
the expansion. This is achieved using something like:
SMLoc::getFromPointer(SMLoc::getPointer() +
(StringRef - CodeInit::getValue()))
The location is lost when producing a CodeInit by string concatenation so
a fallback SMLoc is required (e.g. the Record::getLoc()) but that's pretty
rare for CodeInits.
There's a reasonable case for extending tracking of a couple other Init
objects, for example StringInit's are often parsed and it would be good to
point inside the string when reporting errors about that. However, location
tracking also harms de-duplication. This is fine for CodeInit where there's
only a few hundred of them (~160 for X86) and it may be worth it for
StringInit (~86k up to ~1.9M for roughly 15MB increase for X86).
However the origin tracking would be a _terrible_ idea for IntInit, BitInit,
and UnsetInit. I haven't measured either of those three but BitInit would
most likely be on the order of increasing the current 2 BitInit values up
to billions.
Reviewers: volkan, aditya_nandakumar, bogner, paquette, aemerson
Reviewed By: paquette
Subscribers: javed.absar, kristof.beyls, dexonsmith, llvm-commits, kristina
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D58141
llvm-svn: 355245
2019-03-02 08:12:57 +08:00
|
|
|
#include "llvm/ADT/StringSet.h"
|
2018-04-30 22:59:11 +08:00
|
|
|
#include "llvm/Config/llvm-config.h"
|
2017-06-16 08:43:26 +08:00
|
|
|
#include "llvm/Support/Allocator.h"
|
|
|
|
#include "llvm/Support/Casting.h"
|
2016-08-24 01:14:32 +08:00
|
|
|
#include "llvm/Support/Compiler.h"
|
2012-12-04 00:50:05 +08:00
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
2017-06-16 08:43:26 +08:00
|
|
|
#include "llvm/Support/SMLoc.h"
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2012-12-04 00:50:05 +08:00
|
|
|
#include "llvm/TableGen/Error.h"
|
2017-06-16 08:43:26 +08:00
|
|
|
#include "llvm/TableGen/Record.h"
|
2016-08-24 01:14:32 +08:00
|
|
|
#include <cassert>
|
|
|
|
#include <cstdint>
|
2017-06-16 08:43:26 +08:00
|
|
|
#include <memory>
|
[TableGen] Allow 2^63-1 and 2^63-2 as int literals.
These two values correspond to the 'Empty' and 'Tombstone' special
keys defined by DenseMapInfo<int64_t>, which means that neither one
can be used as a key in DenseMap<int64_t, anything>. Hence, if you try
to use either of those values as an int literal, IntInit::get() fails
an assertion when it tries to insert them into its static cache of
int-literal objects.
Fixed by replacing the DenseMap with a std::map, which doesn't intrude
on the space of legal values of the key type.
Reviewers: nhaehnle, hfinkel, javedabsar, efriedma
Reviewed By: efriedma
Subscribers: fhahn, efriedma, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59016
llvm-svn: 355900
2019-03-12 17:28:19 +08:00
|
|
|
#include <map>
|
2017-06-16 08:43:26 +08:00
|
|
|
#include <string>
|
|
|
|
#include <utility>
|
|
|
|
#include <vector>
|
2005-12-26 13:08:55 +08:00
|
|
|
|
2004-08-01 11:55:39 +08:00
|
|
|
using namespace llvm;
|
2003-10-06 03:27:59 +08:00
|
|
|
|
[tblgen] Track CodeInit origins when possible
Summary:
Add an SMLoc to CodeInit that records the source line it originated from.
This allows tablegen to point precisely at portions of code when reporting
errors within the CodeInit. For example, in the upcoming GlobalISel
combiner, it can report undefined expansions and point at the instance of
the expansion. This is achieved using something like:
SMLoc::getFromPointer(SMLoc::getPointer() +
(StringRef - CodeInit::getValue()))
The location is lost when producing a CodeInit by string concatenation so
a fallback SMLoc is required (e.g. the Record::getLoc()) but that's pretty
rare for CodeInits.
There's a reasonable case for extending tracking of a couple other Init
objects, for example StringInit's are often parsed and it would be good to
point inside the string when reporting errors about that. However, location
tracking also harms de-duplication. This is fine for CodeInit where there's
only a few hundred of them (~160 for X86) and it may be worth it for
StringInit (~86k up to ~1.9M for roughly 15MB increase for X86).
However the origin tracking would be a _terrible_ idea for IntInit, BitInit,
and UnsetInit. I haven't measured either of those three but BitInit would
most likely be on the order of increasing the current 2 BitInit values up
to billions.
Reviewers: volkan, aditya_nandakumar, bogner, paquette, aemerson
Reviewed By: paquette
Subscribers: javed.absar, kristof.beyls, dexonsmith, llvm-commits, kristina
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D58141
llvm-svn: 355245
2019-03-02 08:12:57 +08:00
|
|
|
#define DEBUG_TYPE "tblgen-records"
|
|
|
|
|
2016-12-04 13:48:20 +08:00
|
|
|
static BumpPtrAllocator Allocator;
|
|
|
|
|
2003-10-06 03:27:59 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Type implementations
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2011-07-19 01:02:57 +08:00
|
|
|
BitRecTy BitRecTy::Shared;
|
|
|
|
IntRecTy IntRecTy::Shared;
|
|
|
|
StringRecTy StringRecTy::Shared;
|
|
|
|
DagRecTy DagRecTy::Shared;
|
|
|
|
|
2017-10-15 22:32:27 +08:00
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
2016-01-30 04:50:44 +08:00
|
|
|
LLVM_DUMP_METHOD void RecTy::dump() const { print(errs()); }
|
2017-01-28 10:02:38 +08:00
|
|
|
#endif
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2011-07-19 01:02:57 +08:00
|
|
|
ListRecTy *RecTy::getListTy() {
|
|
|
|
if (!ListTy)
|
2016-12-04 13:48:20 +08:00
|
|
|
ListTy = new(Allocator) ListRecTy(this);
|
|
|
|
return ListTy;
|
2011-07-19 01:02:57 +08:00
|
|
|
}
|
|
|
|
|
2015-05-30 15:36:01 +08:00
|
|
|
bool RecTy::typeIsConvertibleTo(const RecTy *RHS) const {
|
|
|
|
assert(RHS && "NULL pointer");
|
2013-01-07 10:30:19 +08:00
|
|
|
return Kind == RHS->getRecTyKind();
|
|
|
|
}
|
|
|
|
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
bool RecTy::typeIsA(const RecTy *RHS) const { return this == RHS; }
|
|
|
|
|
2015-05-30 15:36:01 +08:00
|
|
|
bool BitRecTy::typeIsConvertibleTo(const RecTy *RHS) const{
|
2015-06-06 09:34:01 +08:00
|
|
|
if (RecTy::typeIsConvertibleTo(RHS) || RHS->getRecTyKind() == IntRecTyKind)
|
2013-01-07 10:30:19 +08:00
|
|
|
return true;
|
2015-06-06 09:34:01 +08:00
|
|
|
if (const BitsRecTy *BitsTy = dyn_cast<BitsRecTy>(RHS))
|
2013-01-07 10:30:19 +08:00
|
|
|
return BitsTy->getNumBits() == 1;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-07-19 01:02:57 +08:00
|
|
|
BitsRecTy *BitsRecTy::get(unsigned Sz) {
|
2016-12-04 13:48:20 +08:00
|
|
|
static std::vector<BitsRecTy*> Shared;
|
2011-07-19 01:02:57 +08:00
|
|
|
if (Sz >= Shared.size())
|
|
|
|
Shared.resize(Sz + 1);
|
2016-12-04 13:48:20 +08:00
|
|
|
BitsRecTy *&Ty = Shared[Sz];
|
2011-07-19 01:02:57 +08:00
|
|
|
if (!Ty)
|
2016-12-04 13:48:20 +08:00
|
|
|
Ty = new(Allocator) BitsRecTy(Sz);
|
|
|
|
return Ty;
|
2011-07-19 01:02:57 +08:00
|
|
|
}
|
|
|
|
|
2007-11-21 06:25:16 +08:00
|
|
|
std::string BitsRecTy::getAsString() const {
|
|
|
|
return "bits<" + utostr(Size) + ">";
|
|
|
|
}
|
|
|
|
|
2015-05-30 15:36:01 +08:00
|
|
|
bool BitsRecTy::typeIsConvertibleTo(const RecTy *RHS) const {
|
|
|
|
if (RecTy::typeIsConvertibleTo(RHS)) //argument and the sender are same type
|
2013-01-07 10:30:19 +08:00
|
|
|
return cast<BitsRecTy>(RHS)->Size == Size;
|
|
|
|
RecTyKind kind = RHS->getRecTyKind();
|
|
|
|
return (kind == BitRecTyKind && Size == 1) || (kind == IntRecTyKind);
|
|
|
|
}
|
|
|
|
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
bool BitsRecTy::typeIsA(const RecTy *RHS) const {
|
|
|
|
if (const BitsRecTy *RHSb = dyn_cast<BitsRecTy>(RHS))
|
|
|
|
return RHSb->Size == Size;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-05-30 15:36:01 +08:00
|
|
|
bool IntRecTy::typeIsConvertibleTo(const RecTy *RHS) const {
|
2013-01-07 10:30:19 +08:00
|
|
|
RecTyKind kind = RHS->getRecTyKind();
|
|
|
|
return kind==BitRecTyKind || kind==BitsRecTyKind || kind==IntRecTyKind;
|
|
|
|
}
|
|
|
|
|
2015-06-01 14:44:18 +08:00
|
|
|
std::string StringRecTy::getAsString() const {
|
|
|
|
return "string";
|
|
|
|
}
|
2006-04-01 05:53:49 +08:00
|
|
|
|
2018-02-22 23:27:03 +08:00
|
|
|
bool StringRecTy::typeIsConvertibleTo(const RecTy *RHS) const {
|
|
|
|
RecTyKind Kind = RHS->getRecTyKind();
|
2020-11-25 02:09:02 +08:00
|
|
|
return Kind == StringRecTyKind;
|
2018-02-22 23:27:03 +08:00
|
|
|
}
|
|
|
|
|
2007-11-21 06:25:16 +08:00
|
|
|
std::string ListRecTy::getAsString() const {
|
2020-09-12 23:50:01 +08:00
|
|
|
return "list<" + ElementTy->getAsString() + ">";
|
2007-11-21 06:25:16 +08:00
|
|
|
}
|
|
|
|
|
2015-05-30 15:36:01 +08:00
|
|
|
bool ListRecTy::typeIsConvertibleTo(const RecTy *RHS) const {
|
|
|
|
if (const auto *ListTy = dyn_cast<ListRecTy>(RHS))
|
2020-09-12 23:50:01 +08:00
|
|
|
return ElementTy->typeIsConvertibleTo(ListTy->getElementType());
|
2013-01-07 10:30:19 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
bool ListRecTy::typeIsA(const RecTy *RHS) const {
|
|
|
|
if (const ListRecTy *RHSl = dyn_cast<ListRecTy>(RHS))
|
|
|
|
return getElementType()->typeIsA(RHSl->getElementType());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-06-01 14:44:18 +08:00
|
|
|
std::string DagRecTy::getAsString() const {
|
|
|
|
return "dag";
|
|
|
|
}
|
|
|
|
|
2018-03-06 21:48:20 +08:00
|
|
|
static void ProfileRecordRecTy(FoldingSetNodeID &ID,
|
|
|
|
ArrayRef<Record *> Classes) {
|
|
|
|
ID.AddInteger(Classes.size());
|
|
|
|
for (Record *R : Classes)
|
|
|
|
ID.AddPointer(R);
|
|
|
|
}
|
|
|
|
|
|
|
|
RecordRecTy *RecordRecTy::get(ArrayRef<Record *> UnsortedClasses) {
|
|
|
|
if (UnsortedClasses.empty()) {
|
|
|
|
static RecordRecTy AnyRecord(0);
|
|
|
|
return &AnyRecord;
|
|
|
|
}
|
|
|
|
|
|
|
|
FoldingSet<RecordRecTy> &ThePool =
|
|
|
|
UnsortedClasses[0]->getRecords().RecordTypePool;
|
|
|
|
|
|
|
|
SmallVector<Record *, 4> Classes(UnsortedClasses.begin(),
|
|
|
|
UnsortedClasses.end());
|
llvm::sort(C.begin(), C.end(), ...) -> llvm::sort(C, ...)
Summary: The convenience wrapper in STLExtras is available since rL342102.
Reviewers: dblaikie, javed.absar, JDevlieghere, andreadb
Subscribers: MatzeB, sanjoy, arsenm, dschuff, mehdi_amini, sdardis, nemanjai, jvesely, nhaehnle, sbc100, jgravelle-google, eraman, aheejin, kbarton, JDevlieghere, javed.absar, gbedwell, jrtc27, mgrang, atanasyan, steven_wu, george.burgess.iv, dexonsmith, kristina, jsji, llvm-commits
Differential Revision: https://reviews.llvm.org/D52573
llvm-svn: 343163
2018-09-27 10:13:45 +08:00
|
|
|
llvm::sort(Classes, [](Record *LHS, Record *RHS) {
|
|
|
|
return LHS->getNameInitAsString() < RHS->getNameInitAsString();
|
|
|
|
});
|
2018-03-06 21:48:20 +08:00
|
|
|
|
|
|
|
FoldingSetNodeID ID;
|
|
|
|
ProfileRecordRecTy(ID, Classes);
|
|
|
|
|
|
|
|
void *IP = nullptr;
|
|
|
|
if (RecordRecTy *Ty = ThePool.FindNodeOrInsertPos(ID, IP))
|
|
|
|
return Ty;
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
// Check for redundancy.
|
|
|
|
for (unsigned i = 0; i < Classes.size(); ++i) {
|
|
|
|
for (unsigned j = 0; j < Classes.size(); ++j) {
|
|
|
|
assert(i == j || !Classes[i]->isSubClassOf(Classes[j]));
|
|
|
|
}
|
|
|
|
assert(&Classes[0]->getRecords() == &Classes[i]->getRecords());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void *Mem = Allocator.Allocate(totalSizeToAlloc<Record *>(Classes.size()),
|
|
|
|
alignof(RecordRecTy));
|
|
|
|
RecordRecTy *Ty = new(Mem) RecordRecTy(Classes.size());
|
|
|
|
std::uninitialized_copy(Classes.begin(), Classes.end(),
|
|
|
|
Ty->getTrailingObjects<Record *>());
|
|
|
|
ThePool.InsertNode(Ty, IP);
|
|
|
|
return Ty;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RecordRecTy::Profile(FoldingSetNodeID &ID) const {
|
|
|
|
ProfileRecordRecTy(ID, getClasses());
|
2011-07-19 01:02:57 +08:00
|
|
|
}
|
|
|
|
|
2007-11-21 06:25:16 +08:00
|
|
|
std::string RecordRecTy::getAsString() const {
|
2018-03-06 21:48:20 +08:00
|
|
|
if (NumClasses == 1)
|
2018-03-19 22:14:20 +08:00
|
|
|
return getClasses()[0]->getNameInitAsString();
|
2018-03-06 21:48:20 +08:00
|
|
|
|
|
|
|
std::string Str = "{";
|
|
|
|
bool First = true;
|
|
|
|
for (Record *R : getClasses()) {
|
|
|
|
if (!First)
|
|
|
|
Str += ", ";
|
|
|
|
First = false;
|
2018-03-19 22:14:20 +08:00
|
|
|
Str += R->getNameInitAsString();
|
2018-03-06 21:48:20 +08:00
|
|
|
}
|
|
|
|
Str += "}";
|
|
|
|
return Str;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RecordRecTy::isSubClassOf(Record *Class) const {
|
|
|
|
return llvm::any_of(getClasses(), [Class](Record *MySuperClass) {
|
|
|
|
return MySuperClass == Class ||
|
|
|
|
MySuperClass->isSubClassOf(Class);
|
|
|
|
});
|
2007-11-21 06:25:16 +08:00
|
|
|
}
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2015-05-30 15:36:01 +08:00
|
|
|
bool RecordRecTy::typeIsConvertibleTo(const RecTy *RHS) const {
|
2018-03-06 21:48:20 +08:00
|
|
|
if (this == RHS)
|
|
|
|
return true;
|
|
|
|
|
2013-01-07 10:30:19 +08:00
|
|
|
const RecordRecTy *RTy = dyn_cast<RecordRecTy>(RHS);
|
|
|
|
if (!RTy)
|
|
|
|
return false;
|
|
|
|
|
2018-03-06 21:48:20 +08:00
|
|
|
return llvm::all_of(RTy->getClasses(), [this](Record *TargetClass) {
|
|
|
|
return isSubClassOf(TargetClass);
|
|
|
|
});
|
|
|
|
}
|
2010-06-18 07:00:16 +08:00
|
|
|
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
bool RecordRecTy::typeIsA(const RecTy *RHS) const {
|
|
|
|
return typeIsConvertibleTo(RHS);
|
|
|
|
}
|
|
|
|
|
2018-03-06 21:48:20 +08:00
|
|
|
static RecordRecTy *resolveRecordTypes(RecordRecTy *T1, RecordRecTy *T2) {
|
|
|
|
SmallVector<Record *, 4> CommonSuperClasses;
|
2021-01-05 03:42:43 +08:00
|
|
|
SmallVector<Record *, 4> Stack(T1->classes_begin(), T1->classes_end());
|
2018-03-06 21:48:20 +08:00
|
|
|
|
|
|
|
while (!Stack.empty()) {
|
2021-01-25 04:18:57 +08:00
|
|
|
Record *R = Stack.pop_back_val();
|
2018-03-06 21:48:20 +08:00
|
|
|
|
|
|
|
if (T2->isSubClassOf(R)) {
|
|
|
|
CommonSuperClasses.push_back(R);
|
|
|
|
} else {
|
|
|
|
R->getDirectSuperClasses(Stack);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return RecordRecTy::get(CommonSuperClasses);
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
2009-06-09 04:23:18 +08:00
|
|
|
RecTy *llvm::resolveTypes(RecTy *T1, RecTy *T2) {
|
2018-03-06 21:48:20 +08:00
|
|
|
if (T1 == T2)
|
|
|
|
return T1;
|
|
|
|
|
|
|
|
if (RecordRecTy *RecTy1 = dyn_cast<RecordRecTy>(T1)) {
|
|
|
|
if (RecordRecTy *RecTy2 = dyn_cast<RecordRecTy>(T2))
|
|
|
|
return resolveRecordTypes(RecTy1, RecTy2);
|
|
|
|
}
|
|
|
|
|
2012-09-19 10:14:59 +08:00
|
|
|
if (T1->typeIsConvertibleTo(T2))
|
|
|
|
return T2;
|
|
|
|
if (T2->typeIsConvertibleTo(T1))
|
|
|
|
return T1;
|
|
|
|
|
2018-03-06 21:48:20 +08:00
|
|
|
if (ListRecTy *ListTy1 = dyn_cast<ListRecTy>(T1)) {
|
|
|
|
if (ListRecTy *ListTy2 = dyn_cast<ListRecTy>(T2)) {
|
|
|
|
RecTy* NewType = resolveTypes(ListTy1->getElementType(),
|
|
|
|
ListTy2->getElementType());
|
|
|
|
if (NewType)
|
|
|
|
return NewType->getListTy();
|
2009-06-09 04:23:18 +08:00
|
|
|
}
|
|
|
|
}
|
2018-03-06 21:48:20 +08:00
|
|
|
|
2014-04-09 12:50:04 +08:00
|
|
|
return nullptr;
|
2009-06-09 04:23:18 +08:00
|
|
|
}
|
|
|
|
|
2003-10-06 03:27:59 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Initializer implementations
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2017-06-16 08:43:26 +08:00
|
|
|
void Init::anchor() {}
|
|
|
|
|
2017-10-15 22:32:27 +08:00
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
2016-01-30 04:50:44 +08:00
|
|
|
LLVM_DUMP_METHOD void Init::dump() const { return print(errs()); }
|
2017-01-28 10:02:38 +08:00
|
|
|
#endif
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
UnsetInit *UnsetInit::get() {
|
|
|
|
static UnsetInit TheInit;
|
2011-07-30 03:07:09 +08:00
|
|
|
return &TheInit;
|
2011-07-30 03:07:07 +08:00
|
|
|
}
|
|
|
|
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
Init *UnsetInit::getCastTo(RecTy *Ty) const {
|
|
|
|
return const_cast<UnsetInit *>(this);
|
|
|
|
}
|
2015-05-30 15:34:51 +08:00
|
|
|
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
Init *UnsetInit::convertInitializerTo(RecTy *Ty) const {
|
2015-05-30 15:34:51 +08:00
|
|
|
return const_cast<UnsetInit *>(this);
|
|
|
|
}
|
2011-12-20 10:50:00 +08:00
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
BitInit *BitInit::get(bool V) {
|
|
|
|
static BitInit True(true);
|
|
|
|
static BitInit False(false);
|
2011-07-30 03:07:10 +08:00
|
|
|
|
|
|
|
return V ? &True : &False;
|
2011-07-30 03:07:07 +08:00
|
|
|
}
|
|
|
|
|
2015-05-30 15:34:51 +08:00
|
|
|
Init *BitInit::convertInitializerTo(RecTy *Ty) const {
|
|
|
|
if (isa<BitRecTy>(Ty))
|
|
|
|
return const_cast<BitInit *>(this);
|
|
|
|
|
|
|
|
if (isa<IntRecTy>(Ty))
|
|
|
|
return IntInit::get(getValue());
|
|
|
|
|
|
|
|
if (auto *BRT = dyn_cast<BitsRecTy>(Ty)) {
|
|
|
|
// Can only convert single bit.
|
|
|
|
if (BRT->getNumBits() == 1)
|
|
|
|
return BitsInit::get(const_cast<BitInit *>(this));
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2011-07-30 03:07:11 +08:00
|
|
|
static void
|
2011-07-30 06:43:06 +08:00
|
|
|
ProfileBitsInit(FoldingSetNodeID &ID, ArrayRef<Init *> Range) {
|
2011-07-30 03:07:11 +08:00
|
|
|
ID.AddInteger(Range.size());
|
|
|
|
|
2015-04-29 15:13:14 +08:00
|
|
|
for (Init *I : Range)
|
|
|
|
ID.AddPointer(I);
|
2011-07-30 03:07:11 +08:00
|
|
|
}
|
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
BitsInit *BitsInit::get(ArrayRef<Init *> Range) {
|
2015-04-24 13:38:48 +08:00
|
|
|
static FoldingSet<BitsInit> ThePool;
|
2011-07-30 03:07:11 +08:00
|
|
|
|
|
|
|
FoldingSetNodeID ID;
|
|
|
|
ProfileBitsInit(ID, Range);
|
|
|
|
|
2014-04-09 12:50:04 +08:00
|
|
|
void *IP = nullptr;
|
2011-07-30 06:43:06 +08:00
|
|
|
if (BitsInit *I = ThePool.FindNodeOrInsertPos(ID, IP))
|
2011-07-30 03:07:11 +08:00
|
|
|
return I;
|
|
|
|
|
2016-12-04 13:48:20 +08:00
|
|
|
void *Mem = Allocator.Allocate(totalSizeToAlloc<Init *>(Range.size()),
|
|
|
|
alignof(BitsInit));
|
|
|
|
BitsInit *I = new(Mem) BitsInit(Range.size());
|
2016-01-19 03:52:24 +08:00
|
|
|
std::uninitialized_copy(Range.begin(), Range.end(),
|
|
|
|
I->getTrailingObjects<Init *>());
|
2011-07-30 03:07:11 +08:00
|
|
|
ThePool.InsertNode(I, IP);
|
|
|
|
return I;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BitsInit::Profile(FoldingSetNodeID &ID) const {
|
2016-01-19 03:52:24 +08:00
|
|
|
ProfileBitsInit(ID, makeArrayRef(getTrailingObjects<Init *>(), NumBits));
|
2011-07-30 03:07:07 +08:00
|
|
|
}
|
|
|
|
|
2015-05-30 15:34:51 +08:00
|
|
|
Init *BitsInit::convertInitializerTo(RecTy *Ty) const {
|
|
|
|
if (isa<BitRecTy>(Ty)) {
|
|
|
|
if (getNumBits() != 1) return nullptr; // Only accept if just one bit!
|
|
|
|
return getBit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto *BRT = dyn_cast<BitsRecTy>(Ty)) {
|
|
|
|
// If the number of bits is right, return it. Otherwise we need to expand
|
|
|
|
// or truncate.
|
|
|
|
if (getNumBits() != BRT->getNumBits()) return nullptr;
|
|
|
|
return const_cast<BitsInit *>(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isa<IntRecTy>(Ty)) {
|
|
|
|
int64_t Result = 0;
|
|
|
|
for (unsigned i = 0, e = getNumBits(); i != e; ++i)
|
|
|
|
if (auto *Bit = dyn_cast<BitInit>(getBit(i)))
|
|
|
|
Result |= static_cast<int64_t>(Bit->getValue()) << i;
|
|
|
|
else
|
|
|
|
return nullptr;
|
|
|
|
return IntInit::get(Result);
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
Init *
|
2016-12-05 14:41:51 +08:00
|
|
|
BitsInit::convertInitializerBitRange(ArrayRef<unsigned> Bits) const {
|
2011-07-30 06:43:06 +08:00
|
|
|
SmallVector<Init *, 16> NewBits(Bits.size());
|
2011-07-30 03:07:00 +08:00
|
|
|
|
2003-10-06 03:27:59 +08:00
|
|
|
for (unsigned i = 0, e = Bits.size(); i != e; ++i) {
|
2011-07-30 03:07:00 +08:00
|
|
|
if (Bits[i] >= getNumBits())
|
2014-04-09 12:50:04 +08:00
|
|
|
return nullptr;
|
2011-07-30 03:07:00 +08:00
|
|
|
NewBits[i] = getBit(Bits[i]);
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
2011-07-30 03:07:07 +08:00
|
|
|
return BitsInit::get(NewBits);
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
2018-03-06 21:48:47 +08:00
|
|
|
bool BitsInit::isConcrete() const {
|
|
|
|
for (unsigned i = 0, e = getNumBits(); i != e; ++i) {
|
|
|
|
if (!getBit(i)->isConcrete())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-11-23 05:05:25 +08:00
|
|
|
std::string BitsInit::getAsString() const {
|
|
|
|
std::string Result = "{ ";
|
2003-10-06 03:27:59 +08:00
|
|
|
for (unsigned i = 0, e = getNumBits(); i != e; ++i) {
|
2007-11-23 05:05:25 +08:00
|
|
|
if (i) Result += ", ";
|
2011-07-30 06:43:06 +08:00
|
|
|
if (Init *Bit = getBit(e-i-1))
|
2007-11-23 05:05:25 +08:00
|
|
|
Result += Bit->getAsString();
|
2003-10-06 03:27:59 +08:00
|
|
|
else
|
2007-11-23 05:05:25 +08:00
|
|
|
Result += "*";
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
2007-11-23 05:05:25 +08:00
|
|
|
return Result + " }";
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// resolveReferences - If there are any field references that refer to fields
|
|
|
|
// that have been filled in, we can propagate the values now.
|
2018-03-05 23:20:51 +08:00
|
|
|
Init *BitsInit::resolveReferences(Resolver &R) const {
|
2003-10-06 03:27:59 +08:00
|
|
|
bool Changed = false;
|
2011-07-30 06:43:06 +08:00
|
|
|
SmallVector<Init *, 16> NewBits(getNumBits());
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2018-03-06 21:48:30 +08:00
|
|
|
Init *CachedBitVarRef = nullptr;
|
|
|
|
Init *CachedBitVarResolved = nullptr;
|
Re-work bit/bits value resolving in tblgen
- This patch is inspired by the failure of the following code snippet
which is used to convert enumerable values into encoding bits to
improve the readability of td files.
class S<int s> {
bits<2> V = !if(!eq(s, 8), {0, 0},
!if(!eq(s, 16), {0, 1},
!if(!eq(s, 32), {1, 0},
!if(!eq(s, 64), {1, 1}, {?, ?}))));
}
Later, PR8330 is found to report not exactly the same bug relevant
issue to bit/bits values.
- Instead of resolving bit/bits values separately through
resolveBitReference(), this patch adds getBit() for all Inits and
resolves bit value by resolving plus getting the specified bit. This
unifies the resolving of bit with other values and removes redundant
logic for resolving bit only. In addition,
BitsInit::resolveReferences() is optimized to take advantage of this
origanization by resolving VarBitInit's variable reference first and
then getting bits from it.
- The type interference in '!if' operator is revised to support possible
combinations of int and bits/bit in MHS and RHS.
- As there may be illegal assignments from integer value to bit, says
assign 2 to a bit, but we only check this during instantiation in some
cases, e.g.
bit V = !if(!eq(x, 17), 0, 2);
Verbose diagnostic message is generated when invalid value is
resolveed to help locating the error.
- PR8330 is fixed as well.
llvm-svn: 163360
2012-09-07 07:32:48 +08:00
|
|
|
|
|
|
|
for (unsigned i = 0, e = getNumBits(); i != e; ++i) {
|
2016-01-19 03:52:24 +08:00
|
|
|
Init *CurBit = getBit(i);
|
2018-03-06 21:48:30 +08:00
|
|
|
Init *NewBit = CurBit;
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2018-03-06 21:48:30 +08:00
|
|
|
if (VarBitInit *CurBitVar = dyn_cast<VarBitInit>(CurBit)) {
|
|
|
|
if (CurBitVar->getBitVar() != CachedBitVarRef) {
|
|
|
|
CachedBitVarRef = CurBitVar->getBitVar();
|
|
|
|
CachedBitVarResolved = CachedBitVarRef->resolveReferences(R);
|
Re-work bit/bits value resolving in tblgen
- This patch is inspired by the failure of the following code snippet
which is used to convert enumerable values into encoding bits to
improve the readability of td files.
class S<int s> {
bits<2> V = !if(!eq(s, 8), {0, 0},
!if(!eq(s, 16), {0, 1},
!if(!eq(s, 32), {1, 0},
!if(!eq(s, 64), {1, 1}, {?, ?}))));
}
Later, PR8330 is found to report not exactly the same bug relevant
issue to bit/bits values.
- Instead of resolving bit/bits values separately through
resolveBitReference(), this patch adds getBit() for all Inits and
resolves bit value by resolving plus getting the specified bit. This
unifies the resolving of bit with other values and removes redundant
logic for resolving bit only. In addition,
BitsInit::resolveReferences() is optimized to take advantage of this
origanization by resolving VarBitInit's variable reference first and
then getting bits from it.
- The type interference in '!if' operator is revised to support possible
combinations of int and bits/bit in MHS and RHS.
- As there may be illegal assignments from integer value to bit, says
assign 2 to a bit, but we only check this during instantiation in some
cases, e.g.
bit V = !if(!eq(x, 17), 0, 2);
Verbose diagnostic message is generated when invalid value is
resolveed to help locating the error.
- PR8330 is fixed as well.
llvm-svn: 163360
2012-09-07 07:32:48 +08:00
|
|
|
}
|
2019-10-15 00:46:21 +08:00
|
|
|
assert(CachedBitVarResolved && "Unresolved bitvar reference");
|
2018-03-06 21:48:30 +08:00
|
|
|
NewBit = CachedBitVarResolved->getBit(CurBitVar->getBitNum());
|
|
|
|
} else {
|
|
|
|
// getBit(0) implicitly converts int and bits<1> values to bit.
|
|
|
|
NewBit = CurBit->resolveReferences(R)->getBit(0);
|
Re-work bit/bits value resolving in tblgen
- This patch is inspired by the failure of the following code snippet
which is used to convert enumerable values into encoding bits to
improve the readability of td files.
class S<int s> {
bits<2> V = !if(!eq(s, 8), {0, 0},
!if(!eq(s, 16), {0, 1},
!if(!eq(s, 32), {1, 0},
!if(!eq(s, 64), {1, 1}, {?, ?}))));
}
Later, PR8330 is found to report not exactly the same bug relevant
issue to bit/bits values.
- Instead of resolving bit/bits values separately through
resolveBitReference(), this patch adds getBit() for all Inits and
resolves bit value by resolving plus getting the specified bit. This
unifies the resolving of bit with other values and removes redundant
logic for resolving bit only. In addition,
BitsInit::resolveReferences() is optimized to take advantage of this
origanization by resolving VarBitInit's variable reference first and
then getting bits from it.
- The type interference in '!if' operator is revised to support possible
combinations of int and bits/bit in MHS and RHS.
- As there may be illegal assignments from integer value to bit, says
assign 2 to a bit, but we only check this during instantiation in some
cases, e.g.
bit V = !if(!eq(x, 17), 0, 2);
Verbose diagnostic message is generated when invalid value is
resolveed to help locating the error.
- PR8330 is fixed as well.
llvm-svn: 163360
2012-09-07 07:32:48 +08:00
|
|
|
}
|
2018-03-06 21:48:30 +08:00
|
|
|
|
|
|
|
if (isa<UnsetInit>(NewBit) && R.keepUnsetBits())
|
|
|
|
NewBit = CurBit;
|
|
|
|
NewBits[i] = NewBit;
|
|
|
|
Changed |= CurBit != NewBit;
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Changed)
|
2011-07-30 03:07:07 +08:00
|
|
|
return BitsInit::get(NewBits);
|
2011-07-30 03:07:00 +08:00
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
return const_cast<BitsInit *>(this);
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
IntInit *IntInit::get(int64_t V) {
|
[TableGen] Allow 2^63-1 and 2^63-2 as int literals.
These two values correspond to the 'Empty' and 'Tombstone' special
keys defined by DenseMapInfo<int64_t>, which means that neither one
can be used as a key in DenseMap<int64_t, anything>. Hence, if you try
to use either of those values as an int literal, IntInit::get() fails
an assertion when it tries to insert them into its static cache of
int-literal objects.
Fixed by replacing the DenseMap with a std::map, which doesn't intrude
on the space of legal values of the key type.
Reviewers: nhaehnle, hfinkel, javedabsar, efriedma
Reviewed By: efriedma
Subscribers: fhahn, efriedma, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59016
llvm-svn: 355900
2019-03-12 17:28:19 +08:00
|
|
|
static std::map<int64_t, IntInit*> ThePool;
|
2011-07-30 03:07:12 +08:00
|
|
|
|
2016-12-04 13:48:20 +08:00
|
|
|
IntInit *&I = ThePool[V];
|
|
|
|
if (!I) I = new(Allocator) IntInit(V);
|
|
|
|
return I;
|
2011-07-30 03:07:07 +08:00
|
|
|
}
|
|
|
|
|
2007-11-23 05:05:25 +08:00
|
|
|
std::string IntInit::getAsString() const {
|
|
|
|
return itostr(Value);
|
|
|
|
}
|
|
|
|
|
2015-05-30 15:34:51 +08:00
|
|
|
static bool canFitInBitfield(int64_t Value, unsigned NumBits) {
|
|
|
|
// For example, with NumBits == 4, we permit Values from [-7 .. 15].
|
|
|
|
return (NumBits >= sizeof(Value) * 8) ||
|
|
|
|
(Value >> NumBits == 0) || (Value >> (NumBits-1) == -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
Init *IntInit::convertInitializerTo(RecTy *Ty) const {
|
|
|
|
if (isa<IntRecTy>(Ty))
|
|
|
|
return const_cast<IntInit *>(this);
|
|
|
|
|
|
|
|
if (isa<BitRecTy>(Ty)) {
|
|
|
|
int64_t Val = getValue();
|
|
|
|
if (Val != 0 && Val != 1) return nullptr; // Only accept 0 or 1 for a bit!
|
|
|
|
return BitInit::get(Val != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto *BRT = dyn_cast<BitsRecTy>(Ty)) {
|
|
|
|
int64_t Value = getValue();
|
|
|
|
// Make sure this bitfield is large enough to hold the integer value.
|
|
|
|
if (!canFitInBitfield(Value, BRT->getNumBits()))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
SmallVector<Init *, 16> NewBits(BRT->getNumBits());
|
|
|
|
for (unsigned i = 0; i != BRT->getNumBits(); ++i)
|
2018-09-22 00:32:49 +08:00
|
|
|
NewBits[i] = BitInit::get(Value & ((i < 64) ? (1LL << i) : 0));
|
2015-05-30 15:34:51 +08:00
|
|
|
|
|
|
|
return BitsInit::get(NewBits);
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
Init *
|
2016-12-05 14:41:51 +08:00
|
|
|
IntInit::convertInitializerBitRange(ArrayRef<unsigned> Bits) const {
|
2011-07-30 06:43:06 +08:00
|
|
|
SmallVector<Init *, 16> NewBits(Bits.size());
|
2003-10-06 03:27:59 +08:00
|
|
|
|
|
|
|
for (unsigned i = 0, e = Bits.size(); i != e; ++i) {
|
2011-07-30 03:07:00 +08:00
|
|
|
if (Bits[i] >= 64)
|
2014-04-09 12:50:04 +08:00
|
|
|
return nullptr;
|
2011-07-30 03:07:00 +08:00
|
|
|
|
2011-07-30 03:07:07 +08:00
|
|
|
NewBits[i] = BitInit::get(Value & (INT64_C(1) << Bits[i]));
|
2011-07-12 02:25:51 +08:00
|
|
|
}
|
2011-07-30 03:07:07 +08:00
|
|
|
return BitsInit::get(NewBits);
|
|
|
|
}
|
|
|
|
|
2021-02-01 23:59:07 +08:00
|
|
|
AnonymousNameInit *AnonymousNameInit::get(unsigned V) {
|
|
|
|
return new (Allocator) AnonymousNameInit(V);
|
|
|
|
}
|
|
|
|
|
|
|
|
StringInit *AnonymousNameInit::getNameInit() const {
|
|
|
|
return StringInit::get(getAsString());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string AnonymousNameInit::getAsString() const {
|
|
|
|
return "anonymous_" + utostr(Value);
|
|
|
|
}
|
|
|
|
|
|
|
|
Init *AnonymousNameInit::resolveReferences(Resolver &R) const {
|
|
|
|
auto *Old = const_cast<Init *>(static_cast<const Init *>(this));
|
|
|
|
auto *New = R.resolve(Old);
|
|
|
|
New = New ? New : Old;
|
|
|
|
if (R.isFinal())
|
|
|
|
if (auto *Anonymous = dyn_cast<AnonymousNameInit>(New))
|
|
|
|
return Anonymous->getNameInit();
|
|
|
|
return New;
|
|
|
|
}
|
|
|
|
|
2020-11-25 02:09:02 +08:00
|
|
|
StringInit *StringInit::get(StringRef V, StringFormat Fmt) {
|
|
|
|
static StringMap<StringInit*, BumpPtrAllocator &> StringPool(Allocator);
|
|
|
|
static StringMap<StringInit*, BumpPtrAllocator &> CodePool(Allocator);
|
2016-07-06 05:22:55 +08:00
|
|
|
|
2020-11-25 02:09:02 +08:00
|
|
|
if (Fmt == SF_String) {
|
|
|
|
auto &Entry = *StringPool.insert(std::make_pair(V, nullptr)).first;
|
|
|
|
if (!Entry.second)
|
|
|
|
Entry.second = new (Allocator) StringInit(Entry.getKey(), Fmt);
|
|
|
|
return Entry.second;
|
|
|
|
} else {
|
|
|
|
auto &Entry = *CodePool.insert(std::make_pair(V, nullptr)).first;
|
|
|
|
if (!Entry.second)
|
|
|
|
Entry.second = new (Allocator) StringInit(Entry.getKey(), Fmt);
|
|
|
|
return Entry.second;
|
|
|
|
}
|
2011-07-30 03:07:07 +08:00
|
|
|
}
|
|
|
|
|
2015-05-30 15:34:51 +08:00
|
|
|
Init *StringInit::convertInitializerTo(RecTy *Ty) const {
|
|
|
|
if (isa<StringRecTy>(Ty))
|
|
|
|
return const_cast<StringInit *>(this);
|
2016-07-06 05:22:55 +08:00
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2011-07-30 03:07:16 +08:00
|
|
|
static void ProfileListInit(FoldingSetNodeID &ID,
|
2011-07-30 06:43:06 +08:00
|
|
|
ArrayRef<Init *> Range,
|
2011-07-30 03:07:16 +08:00
|
|
|
RecTy *EltTy) {
|
|
|
|
ID.AddInteger(Range.size());
|
|
|
|
ID.AddPointer(EltTy);
|
|
|
|
|
2015-04-29 15:13:14 +08:00
|
|
|
for (Init *I : Range)
|
|
|
|
ID.AddPointer(I);
|
2011-07-30 03:07:16 +08:00
|
|
|
}
|
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
ListInit *ListInit::get(ArrayRef<Init *> Range, RecTy *EltTy) {
|
2015-04-24 13:38:48 +08:00
|
|
|
static FoldingSet<ListInit> ThePool;
|
2011-07-30 03:07:16 +08:00
|
|
|
|
|
|
|
FoldingSetNodeID ID;
|
|
|
|
ProfileListInit(ID, Range, EltTy);
|
|
|
|
|
2014-04-09 12:50:04 +08:00
|
|
|
void *IP = nullptr;
|
2011-07-30 06:43:06 +08:00
|
|
|
if (ListInit *I = ThePool.FindNodeOrInsertPos(ID, IP))
|
2011-07-30 03:07:16 +08:00
|
|
|
return I;
|
|
|
|
|
2018-02-22 23:27:12 +08:00
|
|
|
assert(Range.empty() || !isa<TypedInit>(Range[0]) ||
|
|
|
|
cast<TypedInit>(Range[0])->getType()->typeIsConvertibleTo(EltTy));
|
|
|
|
|
2016-12-04 13:48:20 +08:00
|
|
|
void *Mem = Allocator.Allocate(totalSizeToAlloc<Init *>(Range.size()),
|
|
|
|
alignof(ListInit));
|
|
|
|
ListInit *I = new(Mem) ListInit(Range.size(), EltTy);
|
2016-01-19 03:52:24 +08:00
|
|
|
std::uninitialized_copy(Range.begin(), Range.end(),
|
|
|
|
I->getTrailingObjects<Init *>());
|
2011-07-30 03:07:16 +08:00
|
|
|
ThePool.InsertNode(I, IP);
|
|
|
|
return I;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ListInit::Profile(FoldingSetNodeID &ID) const {
|
2015-04-29 15:13:05 +08:00
|
|
|
RecTy *EltTy = cast<ListRecTy>(getType())->getElementType();
|
2011-07-30 03:07:16 +08:00
|
|
|
|
2016-01-19 03:52:24 +08:00
|
|
|
ProfileListInit(ID, getValues(), EltTy);
|
2011-07-12 02:25:51 +08:00
|
|
|
}
|
|
|
|
|
2015-05-30 15:34:51 +08:00
|
|
|
Init *ListInit::convertInitializerTo(RecTy *Ty) const {
|
2016-12-05 14:41:47 +08:00
|
|
|
if (getType() == Ty)
|
|
|
|
return const_cast<ListInit*>(this);
|
|
|
|
|
2015-05-30 15:34:51 +08:00
|
|
|
if (auto *LRT = dyn_cast<ListRecTy>(Ty)) {
|
2016-12-05 14:41:51 +08:00
|
|
|
SmallVector<Init*, 8> Elements;
|
2016-12-05 14:41:47 +08:00
|
|
|
Elements.reserve(getValues().size());
|
2015-05-30 15:34:51 +08:00
|
|
|
|
|
|
|
// Verify that all of the elements of the list are subclasses of the
|
|
|
|
// appropriate class!
|
2016-12-05 14:41:47 +08:00
|
|
|
bool Changed = false;
|
|
|
|
RecTy *ElementType = LRT->getElementType();
|
2015-06-02 12:15:51 +08:00
|
|
|
for (Init *I : getValues())
|
2016-12-05 14:41:47 +08:00
|
|
|
if (Init *CI = I->convertInitializerTo(ElementType)) {
|
2015-05-30 15:34:51 +08:00
|
|
|
Elements.push_back(CI);
|
2016-12-05 14:41:47 +08:00
|
|
|
if (CI != I)
|
|
|
|
Changed = true;
|
2017-08-28 14:47:47 +08:00
|
|
|
} else
|
2015-05-30 15:34:51 +08:00
|
|
|
return nullptr;
|
|
|
|
|
2016-12-05 14:41:47 +08:00
|
|
|
if (!Changed)
|
|
|
|
return const_cast<ListInit*>(this);
|
2018-02-22 23:26:45 +08:00
|
|
|
return ListInit::get(Elements, ElementType);
|
2015-05-30 15:34:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-12-05 14:41:51 +08:00
|
|
|
Init *ListInit::convertInitListSlice(ArrayRef<unsigned> Elements) const {
|
2021-04-09 23:59:18 +08:00
|
|
|
if (Elements.size() == 1) {
|
|
|
|
if (Elements[0] >= size())
|
|
|
|
return nullptr;
|
|
|
|
return getElement(Elements[0]);
|
|
|
|
}
|
2021-04-05 21:13:25 +08:00
|
|
|
|
2016-12-05 14:41:51 +08:00
|
|
|
SmallVector<Init*, 8> Vals;
|
2016-12-05 15:00:44 +08:00
|
|
|
Vals.reserve(Elements.size());
|
|
|
|
for (unsigned Element : Elements) {
|
|
|
|
if (Element >= size())
|
2014-04-09 12:50:04 +08:00
|
|
|
return nullptr;
|
2016-12-05 15:00:44 +08:00
|
|
|
Vals.push_back(getElement(Element));
|
Add initial support for list slices. This currently allows you to do stuff
like this:
def B {
list<int> X = [10, 20, 30, 4, 1, 1231, 20] [2-4,2,2,0-6];
}
... which isn't particularly useful, but more is to come.
llvm-svn: 15247
2004-07-27 07:21:34 +08:00
|
|
|
}
|
2018-02-22 23:26:45 +08:00
|
|
|
return ListInit::get(Vals, getElementType());
|
Add initial support for list slices. This currently allows you to do stuff
like this:
def B {
list<int> X = [10, 20, 30, 4, 1, 1231, 20] [2-4,2,2,0-6];
}
... which isn't particularly useful, but more is to come.
llvm-svn: 15247
2004-07-27 07:21:34 +08:00
|
|
|
}
|
|
|
|
|
2007-02-28 06:08:27 +08:00
|
|
|
Record *ListInit::getElementAsRecord(unsigned i) const {
|
2016-01-19 03:59:05 +08:00
|
|
|
assert(i < NumValues && "List element index out of range!");
|
2016-01-19 03:52:24 +08:00
|
|
|
DefInit *DI = dyn_cast<DefInit>(getElement(i));
|
2014-04-09 12:50:04 +08:00
|
|
|
if (!DI)
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError("Expected record in list!");
|
2007-02-28 06:08:27 +08:00
|
|
|
return DI->getDef();
|
|
|
|
}
|
|
|
|
|
2018-03-05 23:20:51 +08:00
|
|
|
Init *ListInit::resolveReferences(Resolver &R) const {
|
2016-12-05 14:41:51 +08:00
|
|
|
SmallVector<Init*, 8> Resolved;
|
2015-06-02 12:15:57 +08:00
|
|
|
Resolved.reserve(size());
|
2004-07-27 09:01:21 +08:00
|
|
|
bool Changed = false;
|
|
|
|
|
2015-06-02 12:15:51 +08:00
|
|
|
for (Init *CurElt : getValues()) {
|
2018-03-22 01:13:10 +08:00
|
|
|
Init *E = CurElt->resolveReferences(R);
|
|
|
|
Changed |= E != CurElt;
|
2004-07-27 09:01:21 +08:00
|
|
|
Resolved.push_back(E);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Changed)
|
2018-02-22 23:26:45 +08:00
|
|
|
return ListInit::get(Resolved, getElementType());
|
2011-07-30 06:43:06 +08:00
|
|
|
return const_cast<ListInit *>(this);
|
2004-07-27 09:01:21 +08:00
|
|
|
}
|
|
|
|
|
2021-04-14 02:14:30 +08:00
|
|
|
bool ListInit::isComplete() const {
|
|
|
|
for (Init *Element : *this) {
|
|
|
|
if (!Element->isComplete())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-03-06 21:48:47 +08:00
|
|
|
bool ListInit::isConcrete() const {
|
|
|
|
for (Init *Element : *this) {
|
|
|
|
if (!Element->isConcrete())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-11-23 05:05:25 +08:00
|
|
|
std::string ListInit::getAsString() const {
|
|
|
|
std::string Result = "[";
|
2016-12-05 15:00:44 +08:00
|
|
|
const char *sep = "";
|
|
|
|
for (Init *Element : *this) {
|
|
|
|
Result += sep;
|
|
|
|
sep = ", ";
|
|
|
|
Result += Element->getAsString();
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
2007-11-23 05:05:25 +08:00
|
|
|
return Result + "]";
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
Re-work bit/bits value resolving in tblgen
- This patch is inspired by the failure of the following code snippet
which is used to convert enumerable values into encoding bits to
improve the readability of td files.
class S<int s> {
bits<2> V = !if(!eq(s, 8), {0, 0},
!if(!eq(s, 16), {0, 1},
!if(!eq(s, 32), {1, 0},
!if(!eq(s, 64), {1, 1}, {?, ?}))));
}
Later, PR8330 is found to report not exactly the same bug relevant
issue to bit/bits values.
- Instead of resolving bit/bits values separately through
resolveBitReference(), this patch adds getBit() for all Inits and
resolves bit value by resolving plus getting the specified bit. This
unifies the resolving of bit with other values and removes redundant
logic for resolving bit only. In addition,
BitsInit::resolveReferences() is optimized to take advantage of this
origanization by resolving VarBitInit's variable reference first and
then getting bits from it.
- The type interference in '!if' operator is revised to support possible
combinations of int and bits/bit in MHS and RHS.
- As there may be illegal assignments from integer value to bit, says
assign 2 to a bit, but we only check this during instantiation in some
cases, e.g.
bit V = !if(!eq(x, 17), 0, 2);
Verbose diagnostic message is generated when invalid value is
resolveed to help locating the error.
- PR8330 is fixed as well.
llvm-svn: 163360
2012-09-07 07:32:48 +08:00
|
|
|
Init *OpInit::getBit(unsigned Bit) const {
|
|
|
|
if (getType() == BitRecTy::get())
|
|
|
|
return const_cast<OpInit*>(this);
|
|
|
|
return VarBitInit::get(const_cast<OpInit*>(this), Bit);
|
|
|
|
}
|
|
|
|
|
2016-01-19 04:36:06 +08:00
|
|
|
static void
|
|
|
|
ProfileUnOpInit(FoldingSetNodeID &ID, unsigned Opcode, Init *Op, RecTy *Type) {
|
|
|
|
ID.AddInteger(Opcode);
|
|
|
|
ID.AddPointer(Op);
|
|
|
|
ID.AddPointer(Type);
|
|
|
|
}
|
2011-07-30 03:07:18 +08:00
|
|
|
|
2016-01-19 04:36:06 +08:00
|
|
|
UnOpInit *UnOpInit::get(UnaryOp Opc, Init *LHS, RecTy *Type) {
|
|
|
|
static FoldingSet<UnOpInit> ThePool;
|
2011-07-30 03:07:18 +08:00
|
|
|
|
2016-01-19 04:36:06 +08:00
|
|
|
FoldingSetNodeID ID;
|
|
|
|
ProfileUnOpInit(ID, Opc, LHS, Type);
|
|
|
|
|
|
|
|
void *IP = nullptr;
|
|
|
|
if (UnOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP))
|
|
|
|
return I;
|
|
|
|
|
2016-12-04 13:48:20 +08:00
|
|
|
UnOpInit *I = new(Allocator) UnOpInit(Opc, LHS, Type);
|
2016-01-19 04:36:06 +08:00
|
|
|
ThePool.InsertNode(I, IP);
|
|
|
|
return I;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UnOpInit::Profile(FoldingSetNodeID &ID) const {
|
|
|
|
ProfileUnOpInit(ID, getOpcode(), getOperand(), getType());
|
2011-07-30 03:07:07 +08:00
|
|
|
}
|
|
|
|
|
2018-03-19 22:14:20 +08:00
|
|
|
Init *UnOpInit::Fold(Record *CurRec, bool IsFinal) const {
|
2009-05-15 05:22:49 +08:00
|
|
|
switch (getOpcode()) {
|
2017-06-16 08:43:26 +08:00
|
|
|
case CAST:
|
2015-05-28 14:38:28 +08:00
|
|
|
if (isa<StringRecTy>(getType())) {
|
2012-10-11 04:24:47 +08:00
|
|
|
if (StringInit *LHSs = dyn_cast<StringInit>(LHS))
|
2009-06-30 04:05:29 +08:00
|
|
|
return LHSs;
|
2009-05-15 05:22:49 +08:00
|
|
|
|
2012-10-11 04:24:47 +08:00
|
|
|
if (DefInit *LHSd = dyn_cast<DefInit>(LHS))
|
2015-05-28 14:38:32 +08:00
|
|
|
return StringInit::get(LHSd->getAsString());
|
2012-01-31 04:47:04 +08:00
|
|
|
|
2021-01-13 01:55:57 +08:00
|
|
|
if (IntInit *LHSi =
|
|
|
|
dyn_cast_or_null<IntInit>(LHS->convertInitializerTo(IntRecTy::get())))
|
2012-01-31 04:47:04 +08:00
|
|
|
return StringInit::get(LHSi->getAsString());
|
2021-01-13 01:55:57 +08:00
|
|
|
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
} else if (isa<RecordRecTy>(getType())) {
|
2016-12-05 14:00:36 +08:00
|
|
|
if (StringInit *Name = dyn_cast<StringInit>(LHS)) {
|
2018-10-10 18:52:57 +08:00
|
|
|
if (!CurRec && !IsFinal)
|
|
|
|
break;
|
2014-12-13 05:48:03 +08:00
|
|
|
assert(CurRec && "NULL pointer");
|
2018-03-19 22:14:20 +08:00
|
|
|
Record *D;
|
|
|
|
|
|
|
|
// Self-references are allowed, but their resolution is delayed until
|
|
|
|
// the final resolve to ensure that we get the correct type for them.
|
2021-02-01 23:59:07 +08:00
|
|
|
auto *Anonymous = dyn_cast<AnonymousNameInit>(CurRec->getNameInit());
|
|
|
|
if (Name == CurRec->getNameInit() ||
|
|
|
|
(Anonymous && Name == Anonymous->getNameInit())) {
|
2018-03-19 22:14:20 +08:00
|
|
|
if (!IsFinal)
|
|
|
|
break;
|
|
|
|
D = CurRec;
|
|
|
|
} else {
|
|
|
|
D = CurRec->getRecords().getDef(Name->getValue());
|
|
|
|
if (!D) {
|
|
|
|
if (IsFinal)
|
|
|
|
PrintFatalError(CurRec->getLoc(),
|
|
|
|
Twine("Undefined reference to record: '") +
|
|
|
|
Name->getValue() + "'\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-05-15 05:22:49 +08:00
|
|
|
|
2018-03-19 22:14:20 +08:00
|
|
|
DefInit *DI = DefInit::get(D);
|
|
|
|
if (!DI->getType()->typeIsA(getType())) {
|
|
|
|
PrintFatalError(CurRec->getLoc(),
|
|
|
|
Twine("Expected type '") +
|
|
|
|
getType()->getAsString() + "', got '" +
|
|
|
|
DI->getType()->getAsString() + "' in: " +
|
|
|
|
getAsString() + "\n");
|
|
|
|
}
|
|
|
|
return DI;
|
2009-06-30 04:05:29 +08:00
|
|
|
}
|
2009-05-15 05:22:49 +08:00
|
|
|
}
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
|
|
|
|
if (Init *NewInit = LHS->convertInitializerTo(getType()))
|
|
|
|
return NewInit;
|
2009-05-15 05:22:49 +08:00
|
|
|
break;
|
2017-06-16 08:43:26 +08:00
|
|
|
|
2020-10-13 01:35:23 +08:00
|
|
|
case NOT:
|
|
|
|
if (IntInit *LHSi =
|
|
|
|
dyn_cast_or_null<IntInit>(LHS->convertInitializerTo(IntRecTy::get())))
|
|
|
|
return IntInit::get(LHSi->getValue() ? 0 : 1);
|
|
|
|
break;
|
|
|
|
|
2017-06-16 08:43:26 +08:00
|
|
|
case HEAD:
|
2012-10-11 04:24:47 +08:00
|
|
|
if (ListInit *LHSl = dyn_cast<ListInit>(LHS)) {
|
2015-05-16 13:42:13 +08:00
|
|
|
assert(!LHSl->empty() && "Empty list in head");
|
2009-05-15 06:38:31 +08:00
|
|
|
return LHSl->getElement(0);
|
|
|
|
}
|
|
|
|
break;
|
2017-06-16 08:43:26 +08:00
|
|
|
|
|
|
|
case TAIL:
|
2012-10-11 04:24:47 +08:00
|
|
|
if (ListInit *LHSl = dyn_cast<ListInit>(LHS)) {
|
2015-05-16 13:42:13 +08:00
|
|
|
assert(!LHSl->empty() && "Empty list in tail");
|
2011-07-30 03:07:07 +08:00
|
|
|
// Note the +1. We can't just pass the result of getValues()
|
|
|
|
// directly.
|
2018-02-22 23:26:45 +08:00
|
|
|
return ListInit::get(LHSl->getValues().slice(1), LHSl->getElementType());
|
2009-05-15 06:38:31 +08:00
|
|
|
}
|
|
|
|
break;
|
2017-06-16 08:43:26 +08:00
|
|
|
|
2018-02-23 18:46:07 +08:00
|
|
|
case SIZE:
|
|
|
|
if (ListInit *LHSl = dyn_cast<ListInit>(LHS))
|
|
|
|
return IntInit::get(LHSl->size());
|
2020-10-18 22:47:07 +08:00
|
|
|
if (DagInit *LHSd = dyn_cast<DagInit>(LHS))
|
|
|
|
return IntInit::get(LHSd->arg_size());
|
|
|
|
if (StringInit *LHSs = dyn_cast<StringInit>(LHS))
|
|
|
|
return IntInit::get(LHSs->getValue().size());
|
2018-02-23 18:46:07 +08:00
|
|
|
break;
|
|
|
|
|
2017-06-16 08:43:26 +08:00
|
|
|
case EMPTY:
|
2015-05-16 13:42:11 +08:00
|
|
|
if (ListInit *LHSl = dyn_cast<ListInit>(LHS))
|
|
|
|
return IntInit::get(LHSl->empty());
|
2020-10-18 22:47:07 +08:00
|
|
|
if (DagInit *LHSd = dyn_cast<DagInit>(LHS))
|
|
|
|
return IntInit::get(LHSd->arg_empty());
|
2015-05-16 13:42:11 +08:00
|
|
|
if (StringInit *LHSs = dyn_cast<StringInit>(LHS))
|
|
|
|
return IntInit::get(LHSs->getValue().empty());
|
2009-05-15 06:38:31 +08:00
|
|
|
break;
|
2019-12-11 20:02:15 +08:00
|
|
|
|
2020-10-21 01:50:49 +08:00
|
|
|
case GETDAGOP:
|
2019-12-11 20:02:15 +08:00
|
|
|
if (DagInit *Dag = dyn_cast<DagInit>(LHS)) {
|
|
|
|
DefInit *DI = DefInit::get(Dag->getOperatorAsDef({}));
|
|
|
|
if (!DI->getType()->typeIsA(getType())) {
|
|
|
|
PrintFatalError(CurRec->getLoc(),
|
|
|
|
Twine("Expected type '") +
|
|
|
|
getType()->getAsString() + "', got '" +
|
|
|
|
DI->getType()->getAsString() + "' in: " +
|
|
|
|
getAsString() + "\n");
|
|
|
|
} else {
|
|
|
|
return DI;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2009-05-15 06:38:31 +08:00
|
|
|
}
|
2011-07-30 06:43:06 +08:00
|
|
|
return const_cast<UnOpInit *>(this);
|
2009-05-15 05:22:49 +08:00
|
|
|
}
|
|
|
|
|
2018-03-05 23:20:51 +08:00
|
|
|
Init *UnOpInit::resolveReferences(Resolver &R) const {
|
|
|
|
Init *lhs = LHS->resolveReferences(R);
|
2009-11-22 12:24:42 +08:00
|
|
|
|
2018-03-19 22:14:20 +08:00
|
|
|
if (LHS != lhs || (R.isFinal() && getOpcode() == CAST))
|
2018-03-05 23:20:51 +08:00
|
|
|
return (UnOpInit::get(getOpcode(), lhs, getType()))
|
2018-03-19 22:14:20 +08:00
|
|
|
->Fold(R.getCurrentRecord(), R.isFinal());
|
2018-03-19 22:14:04 +08:00
|
|
|
return const_cast<UnOpInit *>(this);
|
2009-05-15 05:22:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string UnOpInit::getAsString() const {
|
|
|
|
std::string Result;
|
2016-01-04 14:28:49 +08:00
|
|
|
switch (getOpcode()) {
|
2009-05-15 05:22:49 +08:00
|
|
|
case CAST: Result = "!cast<" + getType()->getAsString() + ">"; break;
|
2020-10-13 01:35:23 +08:00
|
|
|
case NOT: Result = "!not"; break;
|
2011-01-08 01:05:37 +08:00
|
|
|
case HEAD: Result = "!head"; break;
|
|
|
|
case TAIL: Result = "!tail"; break;
|
2018-02-23 18:46:07 +08:00
|
|
|
case SIZE: Result = "!size"; break;
|
2011-01-08 01:05:37 +08:00
|
|
|
case EMPTY: Result = "!empty"; break;
|
2020-10-21 01:50:49 +08:00
|
|
|
case GETDAGOP: Result = "!getdagop"; break;
|
2009-05-15 05:22:49 +08:00
|
|
|
}
|
|
|
|
return Result + "(" + LHS->getAsString() + ")";
|
|
|
|
}
|
2009-05-15 04:54:48 +08:00
|
|
|
|
2016-01-19 04:36:06 +08:00
|
|
|
static void
|
|
|
|
ProfileBinOpInit(FoldingSetNodeID &ID, unsigned Opcode, Init *LHS, Init *RHS,
|
|
|
|
RecTy *Type) {
|
|
|
|
ID.AddInteger(Opcode);
|
|
|
|
ID.AddPointer(LHS);
|
|
|
|
ID.AddPointer(RHS);
|
|
|
|
ID.AddPointer(Type);
|
|
|
|
}
|
2011-07-30 03:07:19 +08:00
|
|
|
|
2016-01-19 04:36:06 +08:00
|
|
|
BinOpInit *BinOpInit::get(BinaryOp Opc, Init *LHS,
|
|
|
|
Init *RHS, RecTy *Type) {
|
|
|
|
static FoldingSet<BinOpInit> ThePool;
|
2011-07-30 03:07:19 +08:00
|
|
|
|
2016-01-19 04:36:06 +08:00
|
|
|
FoldingSetNodeID ID;
|
|
|
|
ProfileBinOpInit(ID, Opc, LHS, RHS, Type);
|
2011-07-30 03:07:19 +08:00
|
|
|
|
2016-01-19 04:36:06 +08:00
|
|
|
void *IP = nullptr;
|
|
|
|
if (BinOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP))
|
|
|
|
return I;
|
|
|
|
|
2016-12-04 13:48:20 +08:00
|
|
|
BinOpInit *I = new(Allocator) BinOpInit(Opc, LHS, RHS, Type);
|
2016-01-19 04:36:06 +08:00
|
|
|
ThePool.InsertNode(I, IP);
|
|
|
|
return I;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BinOpInit::Profile(FoldingSetNodeID &ID) const {
|
|
|
|
ProfileBinOpInit(ID, getOpcode(), getLHS(), getRHS(), getType());
|
2011-07-30 03:07:07 +08:00
|
|
|
}
|
|
|
|
|
2016-12-05 13:21:18 +08:00
|
|
|
static StringInit *ConcatStringInits(const StringInit *I0,
|
|
|
|
const StringInit *I1) {
|
|
|
|
SmallString<80> Concat(I0->getValue());
|
|
|
|
Concat.append(I1->getValue());
|
2020-11-25 02:09:02 +08:00
|
|
|
return StringInit::get(Concat,
|
|
|
|
StringInit::determineFormat(I0->getFormat(),
|
|
|
|
I1->getFormat()));
|
2016-12-05 13:21:18 +08:00
|
|
|
}
|
|
|
|
|
2020-10-30 23:40:42 +08:00
|
|
|
static StringInit *interleaveStringList(const ListInit *List,
|
|
|
|
const StringInit *Delim) {
|
|
|
|
if (List->size() == 0)
|
|
|
|
return StringInit::get("");
|
2020-12-26 00:50:12 +08:00
|
|
|
StringInit *Element = dyn_cast<StringInit>(List->getElement(0));
|
|
|
|
if (!Element)
|
|
|
|
return nullptr;
|
|
|
|
SmallString<80> Result(Element->getValue());
|
2020-11-25 02:09:02 +08:00
|
|
|
StringInit::StringFormat Fmt = StringInit::SF_String;
|
2020-12-14 20:12:08 +08:00
|
|
|
|
2020-10-30 23:40:42 +08:00
|
|
|
for (unsigned I = 1, E = List->size(); I < E; ++I) {
|
|
|
|
Result.append(Delim->getValue());
|
2020-12-26 00:50:12 +08:00
|
|
|
StringInit *Element = dyn_cast<StringInit>(List->getElement(I));
|
|
|
|
if (!Element)
|
|
|
|
return nullptr;
|
|
|
|
Result.append(Element->getValue());
|
|
|
|
Fmt = StringInit::determineFormat(Fmt, Element->getFormat());
|
2020-10-30 23:40:42 +08:00
|
|
|
}
|
2020-11-25 02:09:02 +08:00
|
|
|
return StringInit::get(Result, Fmt);
|
2020-10-30 23:40:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static StringInit *interleaveIntList(const ListInit *List,
|
|
|
|
const StringInit *Delim) {
|
|
|
|
if (List->size() == 0)
|
|
|
|
return StringInit::get("");
|
2020-12-26 00:50:12 +08:00
|
|
|
IntInit *Element =
|
|
|
|
dyn_cast_or_null<IntInit>(List->getElement(0)
|
|
|
|
->convertInitializerTo(IntRecTy::get()));
|
|
|
|
if (!Element)
|
|
|
|
return nullptr;
|
|
|
|
SmallString<80> Result(Element->getAsString());
|
2020-12-14 20:12:08 +08:00
|
|
|
|
2020-10-30 23:40:42 +08:00
|
|
|
for (unsigned I = 1, E = List->size(); I < E; ++I) {
|
|
|
|
Result.append(Delim->getValue());
|
2020-12-26 00:50:12 +08:00
|
|
|
IntInit *Element =
|
|
|
|
dyn_cast_or_null<IntInit>(List->getElement(I)
|
|
|
|
->convertInitializerTo(IntRecTy::get()));
|
|
|
|
if (!Element)
|
|
|
|
return nullptr;
|
|
|
|
Result.append(Element->getAsString());
|
2020-10-30 23:40:42 +08:00
|
|
|
}
|
|
|
|
return StringInit::get(Result);
|
|
|
|
}
|
|
|
|
|
2018-03-19 22:13:54 +08:00
|
|
|
Init *BinOpInit::getStrConcat(Init *I0, Init *I1) {
|
|
|
|
// Shortcut for the common case of concatenating two strings.
|
|
|
|
if (const StringInit *I0s = dyn_cast<StringInit>(I0))
|
|
|
|
if (const StringInit *I1s = dyn_cast<StringInit>(I1))
|
|
|
|
return ConcatStringInits(I0s, I1s);
|
|
|
|
return BinOpInit::get(BinOpInit::STRCONCAT, I0, I1, StringRecTy::get());
|
|
|
|
}
|
|
|
|
|
2019-03-06 01:16:07 +08:00
|
|
|
static ListInit *ConcatListInits(const ListInit *LHS,
|
|
|
|
const ListInit *RHS) {
|
|
|
|
SmallVector<Init *, 8> Args;
|
2021-01-07 10:27:33 +08:00
|
|
|
llvm::append_range(Args, *LHS);
|
|
|
|
llvm::append_range(Args, *RHS);
|
2019-03-06 01:16:07 +08:00
|
|
|
return ListInit::get(Args, LHS->getElementType());
|
|
|
|
}
|
|
|
|
|
|
|
|
Init *BinOpInit::getListConcat(TypedInit *LHS, Init *RHS) {
|
|
|
|
assert(isa<ListRecTy>(LHS->getType()) && "First arg must be a list");
|
|
|
|
|
|
|
|
// Shortcut for the common case of concatenating two lists.
|
|
|
|
if (const ListInit *LHSList = dyn_cast<ListInit>(LHS))
|
|
|
|
if (const ListInit *RHSList = dyn_cast<ListInit>(RHS))
|
|
|
|
return ConcatListInits(LHSList, RHSList);
|
|
|
|
return BinOpInit::get(BinOpInit::LISTCONCAT, LHS, RHS, LHS->getType());
|
|
|
|
}
|
|
|
|
|
2018-03-19 22:13:37 +08:00
|
|
|
Init *BinOpInit::Fold(Record *CurRec) const {
|
2006-04-01 05:53:49 +08:00
|
|
|
switch (getOpcode()) {
|
2007-05-15 09:23:24 +08:00
|
|
|
case CONCAT: {
|
2012-10-11 04:24:43 +08:00
|
|
|
DagInit *LHSs = dyn_cast<DagInit>(LHS);
|
|
|
|
DagInit *RHSs = dyn_cast<DagInit>(RHS);
|
2007-05-15 09:23:24 +08:00
|
|
|
if (LHSs && RHSs) {
|
2012-10-11 04:24:43 +08:00
|
|
|
DefInit *LOp = dyn_cast<DefInit>(LHSs->getOperator());
|
|
|
|
DefInit *ROp = dyn_cast<DefInit>(RHSs->getOperator());
|
[TableGen] Permit dag operators to be unset.
This is not a new semantic feature. The syntax `(? 1, 2, 3)` was
disallowed by the parser in a dag //expression//, but there were
already ways to sneak a `?` into the operator field of a dag
//value//, e.g. by initializing it from a class template parameter
which is then set to `?` by the instantiating `def`.
This patch makes `?` in the operator slot syntactically legal, so it's
now easy to construct dags with an unset operator. Also, the semantics
of `!con` are relaxed so that it will allow a combination of set and
unset operator fields in the dag nodes it's concatenating, with the
restriction that all the operators that are //not// unset still have
to agree with each other.
Reviewers: hfinkel, nhaehnle
Reviewed By: hfinkel, nhaehnle
Subscribers: hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D71195
2019-12-10 18:34:40 +08:00
|
|
|
if ((!LOp && !isa<UnsetInit>(LHSs->getOperator())) ||
|
|
|
|
(!ROp && !isa<UnsetInit>(RHSs->getOperator())))
|
2018-03-14 19:00:48 +08:00
|
|
|
break;
|
[TableGen] Permit dag operators to be unset.
This is not a new semantic feature. The syntax `(? 1, 2, 3)` was
disallowed by the parser in a dag //expression//, but there were
already ways to sneak a `?` into the operator field of a dag
//value//, e.g. by initializing it from a class template parameter
which is then set to `?` by the instantiating `def`.
This patch makes `?` in the operator slot syntactically legal, so it's
now easy to construct dags with an unset operator. Also, the semantics
of `!con` are relaxed so that it will allow a combination of set and
unset operator fields in the dag nodes it's concatenating, with the
restriction that all the operators that are //not// unset still have
to agree with each other.
Reviewers: hfinkel, nhaehnle
Reviewed By: hfinkel, nhaehnle
Subscribers: hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D71195
2019-12-10 18:34:40 +08:00
|
|
|
if (LOp && ROp && LOp->getDef() != ROp->getDef()) {
|
2018-03-14 19:00:48 +08:00
|
|
|
PrintFatalError(Twine("Concatenated Dag operators do not match: '") +
|
|
|
|
LHSs->getAsString() + "' vs. '" + RHSs->getAsString() +
|
|
|
|
"'");
|
|
|
|
}
|
[TableGen] Permit dag operators to be unset.
This is not a new semantic feature. The syntax `(? 1, 2, 3)` was
disallowed by the parser in a dag //expression//, but there were
already ways to sneak a `?` into the operator field of a dag
//value//, e.g. by initializing it from a class template parameter
which is then set to `?` by the instantiating `def`.
This patch makes `?` in the operator slot syntactically legal, so it's
now easy to construct dags with an unset operator. Also, the semantics
of `!con` are relaxed so that it will allow a combination of set and
unset operator fields in the dag nodes it's concatenating, with the
restriction that all the operators that are //not// unset still have
to agree with each other.
Reviewers: hfinkel, nhaehnle
Reviewed By: hfinkel, nhaehnle
Subscribers: hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D71195
2019-12-10 18:34:40 +08:00
|
|
|
Init *Op = LOp ? LOp : ROp;
|
|
|
|
if (!Op)
|
|
|
|
Op = UnsetInit::get();
|
|
|
|
|
2016-12-05 14:41:51 +08:00
|
|
|
SmallVector<Init*, 8> Args;
|
|
|
|
SmallVector<StringInit*, 8> ArgNames;
|
2007-05-15 09:23:24 +08:00
|
|
|
for (unsigned i = 0, e = LHSs->getNumArgs(); i != e; ++i) {
|
|
|
|
Args.push_back(LHSs->getArg(i));
|
|
|
|
ArgNames.push_back(LHSs->getArgName(i));
|
|
|
|
}
|
|
|
|
for (unsigned i = 0, e = RHSs->getNumArgs(); i != e; ++i) {
|
|
|
|
Args.push_back(RHSs->getArg(i));
|
|
|
|
ArgNames.push_back(RHSs->getArgName(i));
|
|
|
|
}
|
[TableGen] Permit dag operators to be unset.
This is not a new semantic feature. The syntax `(? 1, 2, 3)` was
disallowed by the parser in a dag //expression//, but there were
already ways to sneak a `?` into the operator field of a dag
//value//, e.g. by initializing it from a class template parameter
which is then set to `?` by the instantiating `def`.
This patch makes `?` in the operator slot syntactically legal, so it's
now easy to construct dags with an unset operator. Also, the semantics
of `!con` are relaxed so that it will allow a combination of set and
unset operator fields in the dag nodes it's concatenating, with the
restriction that all the operators that are //not// unset still have
to agree with each other.
Reviewers: hfinkel, nhaehnle
Reviewed By: hfinkel, nhaehnle
Subscribers: hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D71195
2019-12-10 18:34:40 +08:00
|
|
|
return DagInit::get(Op, nullptr, Args, ArgNames);
|
2007-05-15 09:23:24 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2014-05-07 18:13:19 +08:00
|
|
|
case LISTCONCAT: {
|
|
|
|
ListInit *LHSs = dyn_cast<ListInit>(LHS);
|
|
|
|
ListInit *RHSs = dyn_cast<ListInit>(RHS);
|
|
|
|
if (LHSs && RHSs) {
|
2016-12-05 14:41:51 +08:00
|
|
|
SmallVector<Init *, 8> Args;
|
2021-01-07 10:27:33 +08:00
|
|
|
llvm::append_range(Args, *LHSs);
|
|
|
|
llvm::append_range(Args, *RHSs);
|
2018-02-22 23:26:45 +08:00
|
|
|
return ListInit::get(Args, LHSs->getElementType());
|
2014-05-07 18:13:19 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2019-04-11 02:26:36 +08:00
|
|
|
case LISTSPLAT: {
|
|
|
|
TypedInit *Value = dyn_cast<TypedInit>(LHS);
|
|
|
|
IntInit *Size = dyn_cast<IntInit>(RHS);
|
|
|
|
if (Value && Size) {
|
|
|
|
SmallVector<Init *, 8> Args(Size->getValue(), Value);
|
|
|
|
return ListInit::get(Args, Value->getType());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2006-04-01 05:53:49 +08:00
|
|
|
case STRCONCAT: {
|
2012-10-11 04:24:43 +08:00
|
|
|
StringInit *LHSs = dyn_cast<StringInit>(LHS);
|
|
|
|
StringInit *RHSs = dyn_cast<StringInit>(RHS);
|
2016-12-05 13:21:18 +08:00
|
|
|
if (LHSs && RHSs)
|
|
|
|
return ConcatStringInits(LHSs, RHSs);
|
2006-04-01 05:53:49 +08:00
|
|
|
break;
|
|
|
|
}
|
2020-10-30 23:40:42 +08:00
|
|
|
case INTERLEAVE: {
|
|
|
|
ListInit *List = dyn_cast<ListInit>(LHS);
|
|
|
|
StringInit *Delim = dyn_cast<StringInit>(RHS);
|
|
|
|
if (List && Delim) {
|
2020-12-26 00:50:12 +08:00
|
|
|
StringInit *Result;
|
2020-10-30 23:40:42 +08:00
|
|
|
if (isa<StringRecTy>(List->getElementType()))
|
2020-12-26 00:50:12 +08:00
|
|
|
Result = interleaveStringList(List, Delim);
|
2020-10-30 23:40:42 +08:00
|
|
|
else
|
2020-12-26 00:50:12 +08:00
|
|
|
Result = interleaveIntList(List, Delim);
|
|
|
|
if (Result)
|
|
|
|
return Result;
|
2020-10-30 23:40:42 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-03-14 19:00:57 +08:00
|
|
|
case EQ:
|
|
|
|
case NE:
|
|
|
|
case LE:
|
|
|
|
case LT:
|
|
|
|
case GE:
|
|
|
|
case GT: {
|
2020-11-09 00:52:30 +08:00
|
|
|
// First see if we have two bit, bits, or int.
|
|
|
|
IntInit *LHSi =
|
2018-03-14 19:00:57 +08:00
|
|
|
dyn_cast_or_null<IntInit>(LHS->convertInitializerTo(IntRecTy::get()));
|
2020-11-09 00:52:30 +08:00
|
|
|
IntInit *RHSi =
|
2018-03-14 19:00:57 +08:00
|
|
|
dyn_cast_or_null<IntInit>(RHS->convertInitializerTo(IntRecTy::get()));
|
2010-06-17 07:24:12 +08:00
|
|
|
|
2020-11-09 00:52:30 +08:00
|
|
|
if (LHSi && RHSi) {
|
2018-03-14 19:00:57 +08:00
|
|
|
bool Result;
|
|
|
|
switch (getOpcode()) {
|
2020-11-09 00:52:30 +08:00
|
|
|
case EQ: Result = LHSi->getValue() == RHSi->getValue(); break;
|
|
|
|
case NE: Result = LHSi->getValue() != RHSi->getValue(); break;
|
|
|
|
case LE: Result = LHSi->getValue() <= RHSi->getValue(); break;
|
|
|
|
case LT: Result = LHSi->getValue() < RHSi->getValue(); break;
|
|
|
|
case GE: Result = LHSi->getValue() >= RHSi->getValue(); break;
|
|
|
|
case GT: Result = LHSi->getValue() > RHSi->getValue(); break;
|
2018-03-14 19:00:57 +08:00
|
|
|
default: llvm_unreachable("unhandled comparison");
|
|
|
|
}
|
|
|
|
return BitInit::get(Result);
|
|
|
|
}
|
2010-06-17 07:24:12 +08:00
|
|
|
|
2020-11-09 00:52:30 +08:00
|
|
|
// Next try strings.
|
|
|
|
StringInit *LHSs = dyn_cast<StringInit>(LHS);
|
|
|
|
StringInit *RHSs = dyn_cast<StringInit>(RHS);
|
2010-06-17 07:24:12 +08:00
|
|
|
|
2020-11-09 00:52:30 +08:00
|
|
|
if (LHSs && RHSs) {
|
|
|
|
bool Result;
|
|
|
|
switch (getOpcode()) {
|
|
|
|
case EQ: Result = LHSs->getValue() == RHSs->getValue(); break;
|
|
|
|
case NE: Result = LHSs->getValue() != RHSs->getValue(); break;
|
|
|
|
case LE: Result = LHSs->getValue() <= RHSs->getValue(); break;
|
|
|
|
case LT: Result = LHSs->getValue() < RHSs->getValue(); break;
|
|
|
|
case GE: Result = LHSs->getValue() >= RHSs->getValue(); break;
|
|
|
|
case GT: Result = LHSs->getValue() > RHSs->getValue(); break;
|
|
|
|
default: llvm_unreachable("unhandled comparison");
|
2018-03-14 19:00:57 +08:00
|
|
|
}
|
2020-11-09 00:52:30 +08:00
|
|
|
return BitInit::get(Result);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, !eq and !ne can be used with records.
|
|
|
|
if (getOpcode() == EQ || getOpcode() == NE) {
|
|
|
|
DefInit *LHSd = dyn_cast<DefInit>(LHS);
|
|
|
|
DefInit *RHSd = dyn_cast<DefInit>(RHS);
|
|
|
|
if (LHSd && RHSd)
|
|
|
|
return BitInit::get((getOpcode() == EQ) ? LHSd == RHSd
|
|
|
|
: LHSd != RHSd);
|
2018-03-14 19:00:57 +08:00
|
|
|
}
|
2010-01-06 03:11:42 +08:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2020-10-21 01:50:49 +08:00
|
|
|
case SETDAGOP: {
|
2019-12-11 20:02:15 +08:00
|
|
|
DagInit *Dag = dyn_cast<DagInit>(LHS);
|
|
|
|
DefInit *Op = dyn_cast<DefInit>(RHS);
|
|
|
|
if (Dag && Op) {
|
|
|
|
SmallVector<Init*, 8> Args;
|
|
|
|
SmallVector<StringInit*, 8> ArgNames;
|
|
|
|
for (unsigned i = 0, e = Dag->getNumArgs(); i != e; ++i) {
|
|
|
|
Args.push_back(Dag->getArg(i));
|
|
|
|
ArgNames.push_back(Dag->getArgName(i));
|
|
|
|
}
|
|
|
|
return DagInit::get(Op, nullptr, Args, ArgNames);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2013-01-25 22:49:08 +08:00
|
|
|
case ADD:
|
2020-10-24 05:02:39 +08:00
|
|
|
case SUB:
|
2019-03-01 17:46:29 +08:00
|
|
|
case MUL:
|
2014-08-05 17:43:25 +08:00
|
|
|
case AND:
|
2016-11-15 14:49:28 +08:00
|
|
|
case OR:
|
2020-10-13 01:35:23 +08:00
|
|
|
case XOR:
|
2006-04-01 05:53:49 +08:00
|
|
|
case SHL:
|
|
|
|
case SRA:
|
|
|
|
case SRL: {
|
2014-07-18 01:04:27 +08:00
|
|
|
IntInit *LHSi =
|
|
|
|
dyn_cast_or_null<IntInit>(LHS->convertInitializerTo(IntRecTy::get()));
|
|
|
|
IntInit *RHSi =
|
|
|
|
dyn_cast_or_null<IntInit>(RHS->convertInitializerTo(IntRecTy::get()));
|
2006-04-01 05:53:49 +08:00
|
|
|
if (LHSi && RHSi) {
|
2008-10-17 09:33:43 +08:00
|
|
|
int64_t LHSv = LHSi->getValue(), RHSv = RHSi->getValue();
|
|
|
|
int64_t Result;
|
2006-04-01 05:53:49 +08:00
|
|
|
switch (getOpcode()) {
|
2012-02-07 13:05:23 +08:00
|
|
|
default: llvm_unreachable("Bad opcode!");
|
2020-10-24 05:02:39 +08:00
|
|
|
case ADD: Result = LHSv + RHSv; break;
|
|
|
|
case SUB: Result = LHSv - RHSv; break;
|
|
|
|
case MUL: Result = LHSv * RHSv; break;
|
|
|
|
case AND: Result = LHSv & RHSv; break;
|
2020-10-13 01:35:23 +08:00
|
|
|
case OR: Result = LHSv | RHSv; break;
|
|
|
|
case XOR: Result = LHSv ^ RHSv; break;
|
2020-06-17 01:21:09 +08:00
|
|
|
case SHL: Result = (uint64_t)LHSv << (uint64_t)RHSv; break;
|
2006-04-01 05:53:49 +08:00
|
|
|
case SRA: Result = LHSv >> RHSv; break;
|
2008-10-17 09:33:43 +08:00
|
|
|
case SRL: Result = (uint64_t)LHSv >> (uint64_t)RHSv; break;
|
2006-04-01 05:53:49 +08:00
|
|
|
}
|
2011-07-30 03:07:07 +08:00
|
|
|
return IntInit::get(Result);
|
2006-04-01 05:53:49 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-07-30 06:43:06 +08:00
|
|
|
return const_cast<BinOpInit *>(this);
|
2006-04-01 05:53:49 +08:00
|
|
|
}
|
|
|
|
|
2018-03-05 23:20:51 +08:00
|
|
|
Init *BinOpInit::resolveReferences(Resolver &R) const {
|
|
|
|
Init *lhs = LHS->resolveReferences(R);
|
|
|
|
Init *rhs = RHS->resolveReferences(R);
|
2009-11-22 12:24:42 +08:00
|
|
|
|
2006-04-01 05:53:49 +08:00
|
|
|
if (LHS != lhs || RHS != rhs)
|
2018-03-05 23:20:51 +08:00
|
|
|
return (BinOpInit::get(getOpcode(), lhs, rhs, getType()))
|
2018-03-19 22:13:37 +08:00
|
|
|
->Fold(R.getCurrentRecord());
|
2018-03-19 22:14:04 +08:00
|
|
|
return const_cast<BinOpInit *>(this);
|
2006-04-01 05:53:49 +08:00
|
|
|
}
|
|
|
|
|
2007-11-23 05:05:25 +08:00
|
|
|
std::string BinOpInit::getAsString() const {
|
|
|
|
std::string Result;
|
2016-01-04 14:28:49 +08:00
|
|
|
switch (getOpcode()) {
|
2007-11-23 05:05:25 +08:00
|
|
|
case CONCAT: Result = "!con"; break;
|
2013-01-25 22:49:08 +08:00
|
|
|
case ADD: Result = "!add"; break;
|
2020-10-24 05:02:39 +08:00
|
|
|
case SUB: Result = "!sub"; break;
|
2019-03-01 17:46:29 +08:00
|
|
|
case MUL: Result = "!mul"; break;
|
2014-08-05 17:43:25 +08:00
|
|
|
case AND: Result = "!and"; break;
|
2016-11-15 14:49:28 +08:00
|
|
|
case OR: Result = "!or"; break;
|
2020-10-13 01:35:23 +08:00
|
|
|
case XOR: Result = "!xor"; break;
|
2007-11-23 05:05:25 +08:00
|
|
|
case SHL: Result = "!shl"; break;
|
|
|
|
case SRA: Result = "!sra"; break;
|
|
|
|
case SRL: Result = "!srl"; break;
|
2010-01-06 03:11:42 +08:00
|
|
|
case EQ: Result = "!eq"; break;
|
2018-03-14 19:00:57 +08:00
|
|
|
case NE: Result = "!ne"; break;
|
|
|
|
case LE: Result = "!le"; break;
|
|
|
|
case LT: Result = "!lt"; break;
|
|
|
|
case GE: Result = "!ge"; break;
|
|
|
|
case GT: Result = "!gt"; break;
|
2014-05-07 18:13:19 +08:00
|
|
|
case LISTCONCAT: Result = "!listconcat"; break;
|
2019-04-11 02:26:36 +08:00
|
|
|
case LISTSPLAT: Result = "!listsplat"; break;
|
2007-11-23 05:05:25 +08:00
|
|
|
case STRCONCAT: Result = "!strconcat"; break;
|
2020-10-30 23:40:42 +08:00
|
|
|
case INTERLEAVE: Result = "!interleave"; break;
|
2020-10-21 01:50:49 +08:00
|
|
|
case SETDAGOP: Result = "!setdagop"; break;
|
2006-04-01 05:53:49 +08:00
|
|
|
}
|
2007-11-23 05:05:25 +08:00
|
|
|
return Result + "(" + LHS->getAsString() + ", " + RHS->getAsString() + ")";
|
2006-04-01 05:53:49 +08:00
|
|
|
}
|
|
|
|
|
2016-01-19 04:36:06 +08:00
|
|
|
static void
|
|
|
|
ProfileTernOpInit(FoldingSetNodeID &ID, unsigned Opcode, Init *LHS, Init *MHS,
|
|
|
|
Init *RHS, RecTy *Type) {
|
|
|
|
ID.AddInteger(Opcode);
|
|
|
|
ID.AddPointer(LHS);
|
|
|
|
ID.AddPointer(MHS);
|
|
|
|
ID.AddPointer(RHS);
|
|
|
|
ID.AddPointer(Type);
|
|
|
|
}
|
|
|
|
|
|
|
|
TernOpInit *TernOpInit::get(TernaryOp Opc, Init *LHS, Init *MHS, Init *RHS,
|
2015-05-04 09:35:39 +08:00
|
|
|
RecTy *Type) {
|
2016-01-19 04:36:06 +08:00
|
|
|
static FoldingSet<TernOpInit> ThePool;
|
|
|
|
|
|
|
|
FoldingSetNodeID ID;
|
|
|
|
ProfileTernOpInit(ID, Opc, LHS, MHS, RHS, Type);
|
|
|
|
|
|
|
|
void *IP = nullptr;
|
|
|
|
if (TernOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP))
|
|
|
|
return I;
|
|
|
|
|
2016-12-04 13:48:20 +08:00
|
|
|
TernOpInit *I = new(Allocator) TernOpInit(Opc, LHS, MHS, RHS, Type);
|
2016-01-19 04:36:06 +08:00
|
|
|
ThePool.InsertNode(I, IP);
|
|
|
|
return I;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TernOpInit::Profile(FoldingSetNodeID &ID) const {
|
|
|
|
ProfileTernOpInit(ID, getOpcode(), getLHS(), getMHS(), getRHS(), getType());
|
2011-07-30 03:07:07 +08:00
|
|
|
}
|
|
|
|
|
2020-11-06 01:49:21 +08:00
|
|
|
static Init *ItemApply(Init *LHS, Init *MHSe, Init *RHS, Record *CurRec) {
|
TableGen: Reimplement !foreach using the resolving mechanism
Summary:
This changes the syntax of !foreach so that the first "parameter" is
a new syntactic variable: !foreach(x, lst, expr) will define the
variable x within the scope of expr, and evaluation of the !foreach
will substitute elements of the given list (or dag) for x in expr.
Aside from leading to a nicer syntax, this allows more complex
expressions where x is deeply nested, or even constant expressions
in which x does not occur at all.
!foreach is currently not actually used anywhere in trunk, but I
plan to use it in the AMDGPU backend. If out-of-tree targets are
using it, they can adjust to the new syntax very easily.
Change-Id: Ib966694d8ab6542279d6bc358b6f4d767945a805
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits, tpr
Differential Revision: https://reviews.llvm.org/D43651
llvm-svn: 326705
2018-03-05 23:21:04 +08:00
|
|
|
MapResolver R(CurRec);
|
|
|
|
R.set(LHS, MHSe);
|
|
|
|
return RHS->resolveReferences(R);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Init *ForeachDagApply(Init *LHS, DagInit *MHSd, Init *RHS,
|
|
|
|
Record *CurRec) {
|
|
|
|
bool Change = false;
|
2020-11-06 01:49:21 +08:00
|
|
|
Init *Val = ItemApply(LHS, MHSd->getOperator(), RHS, CurRec);
|
TableGen: Reimplement !foreach using the resolving mechanism
Summary:
This changes the syntax of !foreach so that the first "parameter" is
a new syntactic variable: !foreach(x, lst, expr) will define the
variable x within the scope of expr, and evaluation of the !foreach
will substitute elements of the given list (or dag) for x in expr.
Aside from leading to a nicer syntax, this allows more complex
expressions where x is deeply nested, or even constant expressions
in which x does not occur at all.
!foreach is currently not actually used anywhere in trunk, but I
plan to use it in the AMDGPU backend. If out-of-tree targets are
using it, they can adjust to the new syntax very easily.
Change-Id: Ib966694d8ab6542279d6bc358b6f4d767945a805
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits, tpr
Differential Revision: https://reviews.llvm.org/D43651
llvm-svn: 326705
2018-03-05 23:21:04 +08:00
|
|
|
if (Val != MHSd->getOperator())
|
|
|
|
Change = true;
|
|
|
|
|
|
|
|
SmallVector<std::pair<Init *, StringInit *>, 8> NewArgs;
|
|
|
|
for (unsigned int i = 0; i < MHSd->getNumArgs(); ++i) {
|
|
|
|
Init *Arg = MHSd->getArg(i);
|
|
|
|
Init *NewArg;
|
|
|
|
StringInit *ArgName = MHSd->getArgName(i);
|
|
|
|
|
|
|
|
if (DagInit *Argd = dyn_cast<DagInit>(Arg))
|
|
|
|
NewArg = ForeachDagApply(LHS, Argd, RHS, CurRec);
|
|
|
|
else
|
2020-11-06 01:49:21 +08:00
|
|
|
NewArg = ItemApply(LHS, Arg, RHS, CurRec);
|
TableGen: Reimplement !foreach using the resolving mechanism
Summary:
This changes the syntax of !foreach so that the first "parameter" is
a new syntactic variable: !foreach(x, lst, expr) will define the
variable x within the scope of expr, and evaluation of the !foreach
will substitute elements of the given list (or dag) for x in expr.
Aside from leading to a nicer syntax, this allows more complex
expressions where x is deeply nested, or even constant expressions
in which x does not occur at all.
!foreach is currently not actually used anywhere in trunk, but I
plan to use it in the AMDGPU backend. If out-of-tree targets are
using it, they can adjust to the new syntax very easily.
Change-Id: Ib966694d8ab6542279d6bc358b6f4d767945a805
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits, tpr
Differential Revision: https://reviews.llvm.org/D43651
llvm-svn: 326705
2018-03-05 23:21:04 +08:00
|
|
|
|
|
|
|
NewArgs.push_back(std::make_pair(NewArg, ArgName));
|
|
|
|
if (Arg != NewArg)
|
|
|
|
Change = true;
|
2009-05-15 06:23:47 +08:00
|
|
|
}
|
|
|
|
|
TableGen: Reimplement !foreach using the resolving mechanism
Summary:
This changes the syntax of !foreach so that the first "parameter" is
a new syntactic variable: !foreach(x, lst, expr) will define the
variable x within the scope of expr, and evaluation of the !foreach
will substitute elements of the given list (or dag) for x in expr.
Aside from leading to a nicer syntax, this allows more complex
expressions where x is deeply nested, or even constant expressions
in which x does not occur at all.
!foreach is currently not actually used anywhere in trunk, but I
plan to use it in the AMDGPU backend. If out-of-tree targets are
using it, they can adjust to the new syntax very easily.
Change-Id: Ib966694d8ab6542279d6bc358b6f4d767945a805
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits, tpr
Differential Revision: https://reviews.llvm.org/D43651
llvm-svn: 326705
2018-03-05 23:21:04 +08:00
|
|
|
if (Change)
|
|
|
|
return DagInit::get(Val, nullptr, NewArgs);
|
|
|
|
return MHSd;
|
2009-05-15 06:23:47 +08:00
|
|
|
}
|
|
|
|
|
2018-02-10 02:37:55 +08:00
|
|
|
// Applies RHS to all elements of MHS, using LHS as a temp variable.
|
2018-02-22 23:26:45 +08:00
|
|
|
static Init *ForeachHelper(Init *LHS, Init *MHS, Init *RHS, RecTy *Type,
|
TableGen: Reimplement !foreach using the resolving mechanism
Summary:
This changes the syntax of !foreach so that the first "parameter" is
a new syntactic variable: !foreach(x, lst, expr) will define the
variable x within the scope of expr, and evaluation of the !foreach
will substitute elements of the given list (or dag) for x in expr.
Aside from leading to a nicer syntax, this allows more complex
expressions where x is deeply nested, or even constant expressions
in which x does not occur at all.
!foreach is currently not actually used anywhere in trunk, but I
plan to use it in the AMDGPU backend. If out-of-tree targets are
using it, they can adjust to the new syntax very easily.
Change-Id: Ib966694d8ab6542279d6bc358b6f4d767945a805
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits, tpr
Differential Revision: https://reviews.llvm.org/D43651
llvm-svn: 326705
2018-03-05 23:21:04 +08:00
|
|
|
Record *CurRec) {
|
|
|
|
if (DagInit *MHSd = dyn_cast<DagInit>(MHS))
|
|
|
|
return ForeachDagApply(LHS, MHSd, RHS, CurRec);
|
2009-05-15 06:23:47 +08:00
|
|
|
|
TableGen: Reimplement !foreach using the resolving mechanism
Summary:
This changes the syntax of !foreach so that the first "parameter" is
a new syntactic variable: !foreach(x, lst, expr) will define the
variable x within the scope of expr, and evaluation of the !foreach
will substitute elements of the given list (or dag) for x in expr.
Aside from leading to a nicer syntax, this allows more complex
expressions where x is deeply nested, or even constant expressions
in which x does not occur at all.
!foreach is currently not actually used anywhere in trunk, but I
plan to use it in the AMDGPU backend. If out-of-tree targets are
using it, they can adjust to the new syntax very easily.
Change-Id: Ib966694d8ab6542279d6bc358b6f4d767945a805
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits, tpr
Differential Revision: https://reviews.llvm.org/D43651
llvm-svn: 326705
2018-03-05 23:21:04 +08:00
|
|
|
if (ListInit *MHSl = dyn_cast<ListInit>(MHS)) {
|
2016-12-05 14:41:51 +08:00
|
|
|
SmallVector<Init *, 8> NewList(MHSl->begin(), MHSl->end());
|
TableGen: Reimplement !foreach using the resolving mechanism
Summary:
This changes the syntax of !foreach so that the first "parameter" is
a new syntactic variable: !foreach(x, lst, expr) will define the
variable x within the scope of expr, and evaluation of the !foreach
will substitute elements of the given list (or dag) for x in expr.
Aside from leading to a nicer syntax, this allows more complex
expressions where x is deeply nested, or even constant expressions
in which x does not occur at all.
!foreach is currently not actually used anywhere in trunk, but I
plan to use it in the AMDGPU backend. If out-of-tree targets are
using it, they can adjust to the new syntax very easily.
Change-Id: Ib966694d8ab6542279d6bc358b6f4d767945a805
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits, tpr
Differential Revision: https://reviews.llvm.org/D43651
llvm-svn: 326705
2018-03-05 23:21:04 +08:00
|
|
|
|
|
|
|
for (Init *&Item : NewList) {
|
2020-11-06 01:49:21 +08:00
|
|
|
Init *NewItem = ItemApply(LHS, Item, RHS, CurRec);
|
TableGen: Reimplement !foreach using the resolving mechanism
Summary:
This changes the syntax of !foreach so that the first "parameter" is
a new syntactic variable: !foreach(x, lst, expr) will define the
variable x within the scope of expr, and evaluation of the !foreach
will substitute elements of the given list (or dag) for x in expr.
Aside from leading to a nicer syntax, this allows more complex
expressions where x is deeply nested, or even constant expressions
in which x does not occur at all.
!foreach is currently not actually used anywhere in trunk, but I
plan to use it in the AMDGPU backend. If out-of-tree targets are
using it, they can adjust to the new syntax very easily.
Change-Id: Ib966694d8ab6542279d6bc358b6f4d767945a805
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits, tpr
Differential Revision: https://reviews.llvm.org/D43651
llvm-svn: 326705
2018-03-05 23:21:04 +08:00
|
|
|
if (NewItem != Item)
|
|
|
|
Item = NewItem;
|
2009-05-15 06:23:47 +08:00
|
|
|
}
|
TableGen: Reimplement !foreach using the resolving mechanism
Summary:
This changes the syntax of !foreach so that the first "parameter" is
a new syntactic variable: !foreach(x, lst, expr) will define the
variable x within the scope of expr, and evaluation of the !foreach
will substitute elements of the given list (or dag) for x in expr.
Aside from leading to a nicer syntax, this allows more complex
expressions where x is deeply nested, or even constant expressions
in which x does not occur at all.
!foreach is currently not actually used anywhere in trunk, but I
plan to use it in the AMDGPU backend. If out-of-tree targets are
using it, they can adjust to the new syntax very easily.
Change-Id: Ib966694d8ab6542279d6bc358b6f4d767945a805
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits, tpr
Differential Revision: https://reviews.llvm.org/D43651
llvm-svn: 326705
2018-03-05 23:21:04 +08:00
|
|
|
return ListInit::get(NewList, cast<ListRecTy>(Type)->getElementType());
|
2009-05-15 06:23:47 +08:00
|
|
|
}
|
TableGen: Reimplement !foreach using the resolving mechanism
Summary:
This changes the syntax of !foreach so that the first "parameter" is
a new syntactic variable: !foreach(x, lst, expr) will define the
variable x within the scope of expr, and evaluation of the !foreach
will substitute elements of the given list (or dag) for x in expr.
Aside from leading to a nicer syntax, this allows more complex
expressions where x is deeply nested, or even constant expressions
in which x does not occur at all.
!foreach is currently not actually used anywhere in trunk, but I
plan to use it in the AMDGPU backend. If out-of-tree targets are
using it, they can adjust to the new syntax very easily.
Change-Id: Ib966694d8ab6542279d6bc358b6f4d767945a805
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits, tpr
Differential Revision: https://reviews.llvm.org/D43651
llvm-svn: 326705
2018-03-05 23:21:04 +08:00
|
|
|
|
2014-04-09 12:50:04 +08:00
|
|
|
return nullptr;
|
2009-05-15 06:23:47 +08:00
|
|
|
}
|
|
|
|
|
2020-11-06 01:49:21 +08:00
|
|
|
// Evaluates RHS for all elements of MHS, using LHS as a temp variable.
|
|
|
|
// Creates a new list with the elements that evaluated to true.
|
|
|
|
static Init *FilterHelper(Init *LHS, Init *MHS, Init *RHS, RecTy *Type,
|
|
|
|
Record *CurRec) {
|
|
|
|
if (ListInit *MHSl = dyn_cast<ListInit>(MHS)) {
|
|
|
|
SmallVector<Init *, 8> NewList;
|
|
|
|
|
|
|
|
for (Init *Item : MHSl->getValues()) {
|
|
|
|
Init *Include = ItemApply(LHS, Item, RHS, CurRec);
|
|
|
|
if (!Include)
|
|
|
|
return nullptr;
|
|
|
|
if (IntInit *IncludeInt = dyn_cast_or_null<IntInit>(
|
|
|
|
Include->convertInitializerTo(IntRecTy::get()))) {
|
|
|
|
if (IncludeInt->getValue())
|
|
|
|
NewList.push_back(Item);
|
|
|
|
} else {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ListInit::get(NewList, cast<ListRecTy>(Type)->getElementType());
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-03-19 22:13:37 +08:00
|
|
|
Init *TernOpInit::Fold(Record *CurRec) const {
|
2009-05-15 05:54:42 +08:00
|
|
|
switch (getOpcode()) {
|
|
|
|
case SUBST: {
|
2012-10-11 04:24:43 +08:00
|
|
|
DefInit *LHSd = dyn_cast<DefInit>(LHS);
|
|
|
|
VarInit *LHSv = dyn_cast<VarInit>(LHS);
|
|
|
|
StringInit *LHSs = dyn_cast<StringInit>(LHS);
|
2009-05-15 05:54:42 +08:00
|
|
|
|
2012-10-11 04:24:43 +08:00
|
|
|
DefInit *MHSd = dyn_cast<DefInit>(MHS);
|
|
|
|
VarInit *MHSv = dyn_cast<VarInit>(MHS);
|
|
|
|
StringInit *MHSs = dyn_cast<StringInit>(MHS);
|
2009-05-15 05:54:42 +08:00
|
|
|
|
2012-10-11 04:24:43 +08:00
|
|
|
DefInit *RHSd = dyn_cast<DefInit>(RHS);
|
|
|
|
VarInit *RHSv = dyn_cast<VarInit>(RHS);
|
|
|
|
StringInit *RHSs = dyn_cast<StringInit>(RHS);
|
2009-05-15 05:54:42 +08:00
|
|
|
|
2015-05-14 13:54:02 +08:00
|
|
|
if (LHSd && MHSd && RHSd) {
|
|
|
|
Record *Val = RHSd->getDef();
|
|
|
|
if (LHSd->getAsString() == RHSd->getAsString())
|
|
|
|
Val = MHSd->getDef();
|
|
|
|
return DefInit::get(Val);
|
|
|
|
}
|
|
|
|
if (LHSv && MHSv && RHSv) {
|
2020-01-29 03:23:46 +08:00
|
|
|
std::string Val = std::string(RHSv->getName());
|
2015-05-14 13:54:02 +08:00
|
|
|
if (LHSv->getAsString() == RHSv->getAsString())
|
2020-01-29 03:23:46 +08:00
|
|
|
Val = std::string(MHSv->getName());
|
2015-05-14 13:54:02 +08:00
|
|
|
return VarInit::get(Val, getType());
|
|
|
|
}
|
|
|
|
if (LHSs && MHSs && RHSs) {
|
2020-01-29 03:23:46 +08:00
|
|
|
std::string Val = std::string(RHSs->getValue());
|
2015-05-14 13:54:02 +08:00
|
|
|
|
|
|
|
std::string::size_type found;
|
|
|
|
std::string::size_type idx = 0;
|
2015-05-16 13:42:03 +08:00
|
|
|
while (true) {
|
2020-01-29 03:23:46 +08:00
|
|
|
found = Val.find(std::string(LHSs->getValue()), idx);
|
2015-05-16 13:42:03 +08:00
|
|
|
if (found == std::string::npos)
|
|
|
|
break;
|
2020-01-29 03:23:46 +08:00
|
|
|
Val.replace(found, LHSs->getValue().size(),
|
|
|
|
std::string(MHSs->getValue()));
|
2015-05-16 13:42:03 +08:00
|
|
|
idx = found + MHSs->getValue().size();
|
|
|
|
}
|
2015-05-14 13:54:02 +08:00
|
|
|
|
|
|
|
return StringInit::get(Val);
|
2009-05-15 05:54:42 +08:00
|
|
|
}
|
|
|
|
break;
|
2009-11-22 12:24:42 +08:00
|
|
|
}
|
2009-05-15 05:54:42 +08:00
|
|
|
|
|
|
|
case FOREACH: {
|
TableGen: Reimplement !foreach using the resolving mechanism
Summary:
This changes the syntax of !foreach so that the first "parameter" is
a new syntactic variable: !foreach(x, lst, expr) will define the
variable x within the scope of expr, and evaluation of the !foreach
will substitute elements of the given list (or dag) for x in expr.
Aside from leading to a nicer syntax, this allows more complex
expressions where x is deeply nested, or even constant expressions
in which x does not occur at all.
!foreach is currently not actually used anywhere in trunk, but I
plan to use it in the AMDGPU backend. If out-of-tree targets are
using it, they can adjust to the new syntax very easily.
Change-Id: Ib966694d8ab6542279d6bc358b6f4d767945a805
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits, tpr
Differential Revision: https://reviews.llvm.org/D43651
llvm-svn: 326705
2018-03-05 23:21:04 +08:00
|
|
|
if (Init *Result = ForeachHelper(LHS, MHS, RHS, getType(), CurRec))
|
2009-05-15 06:23:47 +08:00
|
|
|
return Result;
|
2009-05-15 05:54:42 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-05-15 07:26:46 +08:00
|
|
|
|
2020-11-06 01:49:21 +08:00
|
|
|
case FILTER: {
|
|
|
|
if (Init *Result = FilterHelper(LHS, MHS, RHS, getType(), CurRec))
|
|
|
|
return Result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-05-15 07:26:46 +08:00
|
|
|
case IF: {
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
if (IntInit *LHSi = dyn_cast_or_null<IntInit>(
|
|
|
|
LHS->convertInitializerTo(IntRecTy::get()))) {
|
2015-05-04 09:35:39 +08:00
|
|
|
if (LHSi->getValue())
|
2009-05-15 07:26:46 +08:00
|
|
|
return MHS;
|
2015-05-04 09:35:39 +08:00
|
|
|
return RHS;
|
2009-05-15 07:26:46 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-03-14 19:00:26 +08:00
|
|
|
|
|
|
|
case DAG: {
|
|
|
|
ListInit *MHSl = dyn_cast<ListInit>(MHS);
|
|
|
|
ListInit *RHSl = dyn_cast<ListInit>(RHS);
|
|
|
|
bool MHSok = MHSl || isa<UnsetInit>(MHS);
|
|
|
|
bool RHSok = RHSl || isa<UnsetInit>(RHS);
|
|
|
|
|
|
|
|
if (isa<UnsetInit>(MHS) && isa<UnsetInit>(RHS))
|
|
|
|
break; // Typically prevented by the parser, but might happen with template args
|
|
|
|
|
|
|
|
if (MHSok && RHSok && (!MHSl || !RHSl || MHSl->size() == RHSl->size())) {
|
|
|
|
SmallVector<std::pair<Init *, StringInit *>, 8> Children;
|
|
|
|
unsigned Size = MHSl ? MHSl->size() : RHSl->size();
|
|
|
|
for (unsigned i = 0; i != Size; ++i) {
|
|
|
|
Init *Node = MHSl ? MHSl->getElement(i) : UnsetInit::get();
|
|
|
|
Init *Name = RHSl ? RHSl->getElement(i) : UnsetInit::get();
|
|
|
|
if (!isa<StringInit>(Name) && !isa<UnsetInit>(Name))
|
|
|
|
return const_cast<TernOpInit *>(this);
|
|
|
|
Children.emplace_back(Node, dyn_cast<StringInit>(Name));
|
|
|
|
}
|
|
|
|
return DagInit::get(LHS, nullptr, Children);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2020-12-16 22:55:16 +08:00
|
|
|
|
|
|
|
case SUBSTR: {
|
|
|
|
StringInit *LHSs = dyn_cast<StringInit>(LHS);
|
|
|
|
IntInit *MHSi = dyn_cast<IntInit>(MHS);
|
|
|
|
IntInit *RHSi = dyn_cast<IntInit>(RHS);
|
|
|
|
if (LHSs && MHSi && RHSi) {
|
|
|
|
int64_t StringSize = LHSs->getValue().size();
|
|
|
|
int64_t Start = MHSi->getValue();
|
|
|
|
int64_t Length = RHSi->getValue();
|
|
|
|
if (Start < 0 || Start > StringSize)
|
|
|
|
PrintError(CurRec->getLoc(),
|
|
|
|
Twine("!substr start position is out of range 0...") +
|
|
|
|
std::to_string(StringSize) + ": " +
|
|
|
|
std::to_string(Start));
|
|
|
|
if (Length < 0)
|
|
|
|
PrintError(CurRec->getLoc(), "!substr length must be nonnegative");
|
|
|
|
return StringInit::get(LHSs->getValue().substr(Start, Length),
|
|
|
|
LHSs->getFormat());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2021-04-26 21:53:35 +08:00
|
|
|
|
|
|
|
case FIND: {
|
|
|
|
StringInit *LHSs = dyn_cast<StringInit>(LHS);
|
|
|
|
StringInit *MHSs = dyn_cast<StringInit>(MHS);
|
|
|
|
IntInit *RHSi = dyn_cast<IntInit>(RHS);
|
|
|
|
if (LHSs && MHSs && RHSi) {
|
|
|
|
int64_t SourceSize = LHSs->getValue().size();
|
|
|
|
int64_t Start = RHSi->getValue();
|
|
|
|
if (Start < 0 || Start > SourceSize)
|
|
|
|
PrintError(CurRec->getLoc(),
|
|
|
|
Twine("!find start position is out of range 0...") +
|
|
|
|
std::to_string(SourceSize) + ": " +
|
|
|
|
std::to_string(Start));
|
|
|
|
auto I = LHSs->getValue().find(MHSs->getValue(), Start);
|
|
|
|
if (I == std::string::npos)
|
|
|
|
return IntInit::get(-1);
|
|
|
|
return IntInit::get(I);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2009-05-15 05:54:42 +08:00
|
|
|
}
|
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
return const_cast<TernOpInit *>(this);
|
2009-05-15 05:54:42 +08:00
|
|
|
}
|
|
|
|
|
2018-03-05 23:20:51 +08:00
|
|
|
Init *TernOpInit::resolveReferences(Resolver &R) const {
|
|
|
|
Init *lhs = LHS->resolveReferences(R);
|
2009-06-09 03:16:56 +08:00
|
|
|
|
2016-01-04 14:28:49 +08:00
|
|
|
if (getOpcode() == IF && lhs != LHS) {
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
if (IntInit *Value = dyn_cast_or_null<IntInit>(
|
|
|
|
lhs->convertInitializerTo(IntRecTy::get()))) {
|
2009-06-09 03:16:56 +08:00
|
|
|
// Short-circuit
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
if (Value->getValue())
|
|
|
|
return MHS->resolveReferences(R);
|
|
|
|
return RHS->resolveReferences(R);
|
2009-06-09 03:16:56 +08:00
|
|
|
}
|
|
|
|
}
|
2009-11-22 12:24:42 +08:00
|
|
|
|
2018-03-05 23:20:51 +08:00
|
|
|
Init *mhs = MHS->resolveReferences(R);
|
TableGen: Reimplement !foreach using the resolving mechanism
Summary:
This changes the syntax of !foreach so that the first "parameter" is
a new syntactic variable: !foreach(x, lst, expr) will define the
variable x within the scope of expr, and evaluation of the !foreach
will substitute elements of the given list (or dag) for x in expr.
Aside from leading to a nicer syntax, this allows more complex
expressions where x is deeply nested, or even constant expressions
in which x does not occur at all.
!foreach is currently not actually used anywhere in trunk, but I
plan to use it in the AMDGPU backend. If out-of-tree targets are
using it, they can adjust to the new syntax very easily.
Change-Id: Ib966694d8ab6542279d6bc358b6f4d767945a805
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits, tpr
Differential Revision: https://reviews.llvm.org/D43651
llvm-svn: 326705
2018-03-05 23:21:04 +08:00
|
|
|
Init *rhs;
|
|
|
|
|
2020-11-06 01:49:21 +08:00
|
|
|
if (getOpcode() == FOREACH || getOpcode() == FILTER) {
|
TableGen: Reimplement !foreach using the resolving mechanism
Summary:
This changes the syntax of !foreach so that the first "parameter" is
a new syntactic variable: !foreach(x, lst, expr) will define the
variable x within the scope of expr, and evaluation of the !foreach
will substitute elements of the given list (or dag) for x in expr.
Aside from leading to a nicer syntax, this allows more complex
expressions where x is deeply nested, or even constant expressions
in which x does not occur at all.
!foreach is currently not actually used anywhere in trunk, but I
plan to use it in the AMDGPU backend. If out-of-tree targets are
using it, they can adjust to the new syntax very easily.
Change-Id: Ib966694d8ab6542279d6bc358b6f4d767945a805
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits, tpr
Differential Revision: https://reviews.llvm.org/D43651
llvm-svn: 326705
2018-03-05 23:21:04 +08:00
|
|
|
ShadowResolver SR(R);
|
|
|
|
SR.addShadow(lhs);
|
|
|
|
rhs = RHS->resolveReferences(SR);
|
|
|
|
} else {
|
|
|
|
rhs = RHS->resolveReferences(R);
|
|
|
|
}
|
2009-06-09 03:16:56 +08:00
|
|
|
|
2009-05-15 05:54:42 +08:00
|
|
|
if (LHS != lhs || MHS != mhs || RHS != rhs)
|
2018-03-05 23:20:51 +08:00
|
|
|
return (TernOpInit::get(getOpcode(), lhs, mhs, rhs, getType()))
|
2018-03-19 22:13:37 +08:00
|
|
|
->Fold(R.getCurrentRecord());
|
2018-03-19 22:14:04 +08:00
|
|
|
return const_cast<TernOpInit *>(this);
|
2009-05-15 05:54:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string TernOpInit::getAsString() const {
|
|
|
|
std::string Result;
|
2018-05-02 21:17:26 +08:00
|
|
|
bool UnquotedLHS = false;
|
2016-01-04 14:28:49 +08:00
|
|
|
switch (getOpcode()) {
|
2020-12-16 22:55:16 +08:00
|
|
|
case DAG: Result = "!dag"; break;
|
2020-12-21 23:45:30 +08:00
|
|
|
case FILTER: Result = "!filter"; UnquotedLHS = true; break;
|
2020-12-16 22:55:16 +08:00
|
|
|
case FOREACH: Result = "!foreach"; UnquotedLHS = true; break;
|
2009-11-22 12:24:42 +08:00
|
|
|
case IF: Result = "!if"; break;
|
2020-12-16 22:55:16 +08:00
|
|
|
case SUBST: Result = "!subst"; break;
|
|
|
|
case SUBSTR: Result = "!substr"; break;
|
2021-04-26 21:53:35 +08:00
|
|
|
case FIND: Result = "!find"; break;
|
2015-04-30 13:54:22 +08:00
|
|
|
}
|
2018-05-02 21:17:26 +08:00
|
|
|
return (Result + "(" +
|
|
|
|
(UnquotedLHS ? LHS->getAsUnquotedString() : LHS->getAsString()) +
|
|
|
|
", " + MHS->getAsString() + ", " + RHS->getAsString() + ")");
|
2009-05-15 05:54:42 +08:00
|
|
|
}
|
2009-04-24 05:25:15 +08:00
|
|
|
|
2021-06-07 23:56:26 +08:00
|
|
|
static void ProfileFoldOpInit(FoldingSetNodeID &ID, Init *Start, Init *List,
|
|
|
|
Init *A, Init *B, Init *Expr, RecTy *Type) {
|
2018-03-06 21:49:16 +08:00
|
|
|
ID.AddPointer(Start);
|
|
|
|
ID.AddPointer(List);
|
|
|
|
ID.AddPointer(A);
|
|
|
|
ID.AddPointer(B);
|
|
|
|
ID.AddPointer(Expr);
|
|
|
|
ID.AddPointer(Type);
|
|
|
|
}
|
|
|
|
|
|
|
|
FoldOpInit *FoldOpInit::get(Init *Start, Init *List, Init *A, Init *B,
|
|
|
|
Init *Expr, RecTy *Type) {
|
|
|
|
static FoldingSet<FoldOpInit> ThePool;
|
|
|
|
|
|
|
|
FoldingSetNodeID ID;
|
|
|
|
ProfileFoldOpInit(ID, Start, List, A, B, Expr, Type);
|
|
|
|
|
|
|
|
void *IP = nullptr;
|
|
|
|
if (FoldOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP))
|
|
|
|
return I;
|
|
|
|
|
|
|
|
FoldOpInit *I = new (Allocator) FoldOpInit(Start, List, A, B, Expr, Type);
|
|
|
|
ThePool.InsertNode(I, IP);
|
|
|
|
return I;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FoldOpInit::Profile(FoldingSetNodeID &ID) const {
|
|
|
|
ProfileFoldOpInit(ID, Start, List, A, B, Expr, getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
Init *FoldOpInit::Fold(Record *CurRec) const {
|
|
|
|
if (ListInit *LI = dyn_cast<ListInit>(List)) {
|
|
|
|
Init *Accum = Start;
|
|
|
|
for (Init *Elt : *LI) {
|
|
|
|
MapResolver R(CurRec);
|
|
|
|
R.set(A, Accum);
|
|
|
|
R.set(B, Elt);
|
|
|
|
Accum = Expr->resolveReferences(R);
|
|
|
|
}
|
|
|
|
return Accum;
|
|
|
|
}
|
|
|
|
return const_cast<FoldOpInit *>(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
Init *FoldOpInit::resolveReferences(Resolver &R) const {
|
|
|
|
Init *NewStart = Start->resolveReferences(R);
|
|
|
|
Init *NewList = List->resolveReferences(R);
|
|
|
|
ShadowResolver SR(R);
|
|
|
|
SR.addShadow(A);
|
|
|
|
SR.addShadow(B);
|
|
|
|
Init *NewExpr = Expr->resolveReferences(SR);
|
|
|
|
|
|
|
|
if (Start == NewStart && List == NewList && Expr == NewExpr)
|
|
|
|
return const_cast<FoldOpInit *>(this);
|
|
|
|
|
|
|
|
return get(NewStart, NewList, A, B, NewExpr, getType())
|
|
|
|
->Fold(R.getCurrentRecord());
|
|
|
|
}
|
|
|
|
|
|
|
|
Init *FoldOpInit::getBit(unsigned Bit) const {
|
|
|
|
return VarBitInit::get(const_cast<FoldOpInit *>(this), Bit);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string FoldOpInit::getAsString() const {
|
|
|
|
return (Twine("!foldl(") + Start->getAsString() + ", " + List->getAsString() +
|
|
|
|
", " + A->getAsUnquotedString() + ", " + B->getAsUnquotedString() +
|
|
|
|
", " + Expr->getAsString() + ")")
|
|
|
|
.str();
|
|
|
|
}
|
|
|
|
|
2018-03-09 20:24:06 +08:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2016-12-05 14:00:36 +08:00
|
|
|
RecTy *TypedInit::getFieldType(StringInit *FieldName) const {
|
2018-03-06 21:48:20 +08:00
|
|
|
if (RecordRecTy *RecordType = dyn_cast<RecordRecTy>(getType())) {
|
|
|
|
for (Record *Rec : RecordType->getClasses()) {
|
|
|
|
if (RecordVal *Field = Rec->getValue(FieldName))
|
|
|
|
return Field->getType();
|
|
|
|
}
|
|
|
|
}
|
2014-04-09 12:50:04 +08:00
|
|
|
return nullptr;
|
2010-09-04 05:00:49 +08:00
|
|
|
}
|
|
|
|
|
2015-05-30 15:34:51 +08:00
|
|
|
Init *
|
|
|
|
TypedInit::convertInitializerTo(RecTy *Ty) const {
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
if (getType() == Ty || getType()->typeIsA(Ty))
|
|
|
|
return const_cast<TypedInit *>(this);
|
2015-05-30 15:34:51 +08:00
|
|
|
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
if (isa<BitRecTy>(getType()) && isa<BitsRecTy>(Ty) &&
|
|
|
|
cast<BitsRecTy>(Ty)->getNumBits() == 1)
|
|
|
|
return BitsInit::get({const_cast<TypedInit *>(this)});
|
2015-05-30 15:34:51 +08:00
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-12-05 14:41:51 +08:00
|
|
|
Init *TypedInit::convertInitializerBitRange(ArrayRef<unsigned> Bits) const {
|
2012-10-05 11:31:58 +08:00
|
|
|
BitsRecTy *T = dyn_cast<BitsRecTy>(getType());
|
2014-04-09 12:50:04 +08:00
|
|
|
if (!T) return nullptr; // Cannot subscript a non-bits variable.
|
2003-10-06 03:27:59 +08:00
|
|
|
unsigned NumBits = T->getNumBits();
|
|
|
|
|
2016-12-05 15:00:44 +08:00
|
|
|
SmallVector<Init *, 16> NewBits;
|
|
|
|
NewBits.reserve(Bits.size());
|
|
|
|
for (unsigned Bit : Bits) {
|
|
|
|
if (Bit >= NumBits)
|
2014-04-09 12:50:04 +08:00
|
|
|
return nullptr;
|
2011-07-30 03:07:00 +08:00
|
|
|
|
2016-12-05 15:00:44 +08:00
|
|
|
NewBits.push_back(VarBitInit::get(const_cast<TypedInit *>(this), Bit));
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
2011-07-30 03:07:07 +08:00
|
|
|
return BitsInit::get(NewBits);
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
Init *TypedInit::getCastTo(RecTy *Ty) const {
|
|
|
|
// Handle the common case quickly
|
|
|
|
if (getType() == Ty || getType()->typeIsA(Ty))
|
|
|
|
return const_cast<TypedInit *>(this);
|
|
|
|
|
|
|
|
if (Init *Converted = convertInitializerTo(Ty)) {
|
|
|
|
assert(!isa<TypedInit>(Converted) ||
|
|
|
|
cast<TypedInit>(Converted)->getType()->typeIsA(Ty));
|
|
|
|
return Converted;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!getType()->typeIsConvertibleTo(Ty))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return UnOpInit::get(UnOpInit::CAST, const_cast<TypedInit *>(this), Ty)
|
2018-03-19 22:13:37 +08:00
|
|
|
->Fold(nullptr);
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
}
|
|
|
|
|
2016-12-05 14:41:51 +08:00
|
|
|
Init *TypedInit::convertInitListSlice(ArrayRef<unsigned> Elements) const {
|
2012-10-05 11:31:58 +08:00
|
|
|
ListRecTy *T = dyn_cast<ListRecTy>(getType());
|
2014-04-09 12:50:04 +08:00
|
|
|
if (!T) return nullptr; // Cannot subscript a non-list variable.
|
2004-07-27 09:01:21 +08:00
|
|
|
|
|
|
|
if (Elements.size() == 1)
|
2011-07-30 06:43:06 +08:00
|
|
|
return VarListElementInit::get(const_cast<TypedInit *>(this), Elements[0]);
|
2004-07-27 09:01:21 +08:00
|
|
|
|
2016-12-05 14:41:51 +08:00
|
|
|
SmallVector<Init*, 8> ListInits;
|
2004-07-27 09:01:21 +08:00
|
|
|
ListInits.reserve(Elements.size());
|
2016-12-05 15:00:44 +08:00
|
|
|
for (unsigned Element : Elements)
|
2011-07-30 06:43:06 +08:00
|
|
|
ListInits.push_back(VarListElementInit::get(const_cast<TypedInit *>(this),
|
2016-12-05 15:00:44 +08:00
|
|
|
Element));
|
2018-02-22 23:26:45 +08:00
|
|
|
return ListInit::get(ListInits, T->getElementType());
|
2004-07-27 09:01:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-12-04 13:48:03 +08:00
|
|
|
VarInit *VarInit::get(StringRef VN, RecTy *T) {
|
2011-10-19 21:02:33 +08:00
|
|
|
Init *Value = StringInit::get(VN);
|
|
|
|
return VarInit::get(Value, T);
|
|
|
|
}
|
|
|
|
|
|
|
|
VarInit *VarInit::get(Init *VN, RecTy *T) {
|
2017-06-16 08:43:26 +08:00
|
|
|
using Key = std::pair<RecTy *, Init *>;
|
2016-12-04 13:48:20 +08:00
|
|
|
static DenseMap<Key, VarInit*> ThePool;
|
2011-07-30 03:07:21 +08:00
|
|
|
|
|
|
|
Key TheKey(std::make_pair(T, VN));
|
|
|
|
|
2016-12-04 13:48:20 +08:00
|
|
|
VarInit *&I = ThePool[TheKey];
|
|
|
|
if (!I)
|
|
|
|
I = new(Allocator) VarInit(VN, T);
|
|
|
|
return I;
|
2011-07-30 03:07:07 +08:00
|
|
|
}
|
|
|
|
|
2016-12-04 13:48:16 +08:00
|
|
|
StringRef VarInit::getName() const {
|
2015-04-29 15:13:05 +08:00
|
|
|
StringInit *NameString = cast<StringInit>(getNameInit());
|
2011-10-19 21:02:33 +08:00
|
|
|
return NameString->getValue();
|
|
|
|
}
|
|
|
|
|
Re-work bit/bits value resolving in tblgen
- This patch is inspired by the failure of the following code snippet
which is used to convert enumerable values into encoding bits to
improve the readability of td files.
class S<int s> {
bits<2> V = !if(!eq(s, 8), {0, 0},
!if(!eq(s, 16), {0, 1},
!if(!eq(s, 32), {1, 0},
!if(!eq(s, 64), {1, 1}, {?, ?}))));
}
Later, PR8330 is found to report not exactly the same bug relevant
issue to bit/bits values.
- Instead of resolving bit/bits values separately through
resolveBitReference(), this patch adds getBit() for all Inits and
resolves bit value by resolving plus getting the specified bit. This
unifies the resolving of bit with other values and removes redundant
logic for resolving bit only. In addition,
BitsInit::resolveReferences() is optimized to take advantage of this
origanization by resolving VarBitInit's variable reference first and
then getting bits from it.
- The type interference in '!if' operator is revised to support possible
combinations of int and bits/bit in MHS and RHS.
- As there may be illegal assignments from integer value to bit, says
assign 2 to a bit, but we only check this during instantiation in some
cases, e.g.
bit V = !if(!eq(x, 17), 0, 2);
Verbose diagnostic message is generated when invalid value is
resolveed to help locating the error.
- PR8330 is fixed as well.
llvm-svn: 163360
2012-09-07 07:32:48 +08:00
|
|
|
Init *VarInit::getBit(unsigned Bit) const {
|
|
|
|
if (getType() == BitRecTy::get())
|
|
|
|
return const_cast<VarInit*>(this);
|
|
|
|
return VarBitInit::get(const_cast<VarInit*>(this), Bit);
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
2018-03-05 23:20:51 +08:00
|
|
|
Init *VarInit::resolveReferences(Resolver &R) const {
|
|
|
|
if (Init *Val = R.resolve(VarName))
|
|
|
|
return Val;
|
2011-07-30 06:43:06 +08:00
|
|
|
return const_cast<VarInit *>(this);
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
2005-04-22 08:00:37 +08:00
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
VarBitInit *VarBitInit::get(TypedInit *T, unsigned B) {
|
2017-06-16 08:43:26 +08:00
|
|
|
using Key = std::pair<TypedInit *, unsigned>;
|
2016-12-04 13:48:20 +08:00
|
|
|
static DenseMap<Key, VarBitInit*> ThePool;
|
2011-07-30 03:07:22 +08:00
|
|
|
|
|
|
|
Key TheKey(std::make_pair(T, B));
|
|
|
|
|
2016-12-04 13:48:20 +08:00
|
|
|
VarBitInit *&I = ThePool[TheKey];
|
|
|
|
if (!I)
|
|
|
|
I = new(Allocator) VarBitInit(T, B);
|
|
|
|
return I;
|
2011-07-30 03:07:07 +08:00
|
|
|
}
|
|
|
|
|
2007-11-23 05:05:25 +08:00
|
|
|
std::string VarBitInit::getAsString() const {
|
2015-05-04 09:35:39 +08:00
|
|
|
return TI->getAsString() + "{" + utostr(Bit) + "}";
|
2007-11-23 05:05:25 +08:00
|
|
|
}
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2018-03-05 23:20:51 +08:00
|
|
|
Init *VarBitInit::resolveReferences(Resolver &R) const {
|
|
|
|
Init *I = TI->resolveReferences(R);
|
Re-work bit/bits value resolving in tblgen
- This patch is inspired by the failure of the following code snippet
which is used to convert enumerable values into encoding bits to
improve the readability of td files.
class S<int s> {
bits<2> V = !if(!eq(s, 8), {0, 0},
!if(!eq(s, 16), {0, 1},
!if(!eq(s, 32), {1, 0},
!if(!eq(s, 64), {1, 1}, {?, ?}))));
}
Later, PR8330 is found to report not exactly the same bug relevant
issue to bit/bits values.
- Instead of resolving bit/bits values separately through
resolveBitReference(), this patch adds getBit() for all Inits and
resolves bit value by resolving plus getting the specified bit. This
unifies the resolving of bit with other values and removes redundant
logic for resolving bit only. In addition,
BitsInit::resolveReferences() is optimized to take advantage of this
origanization by resolving VarBitInit's variable reference first and
then getting bits from it.
- The type interference in '!if' operator is revised to support possible
combinations of int and bits/bit in MHS and RHS.
- As there may be illegal assignments from integer value to bit, says
assign 2 to a bit, but we only check this during instantiation in some
cases, e.g.
bit V = !if(!eq(x, 17), 0, 2);
Verbose diagnostic message is generated when invalid value is
resolveed to help locating the error.
- PR8330 is fixed as well.
llvm-svn: 163360
2012-09-07 07:32:48 +08:00
|
|
|
if (TI != I)
|
|
|
|
return I->getBit(getBitNum());
|
|
|
|
|
|
|
|
return const_cast<VarBitInit*>(this);
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
VarListElementInit *VarListElementInit::get(TypedInit *T,
|
|
|
|
unsigned E) {
|
2017-06-16 08:43:26 +08:00
|
|
|
using Key = std::pair<TypedInit *, unsigned>;
|
2016-12-04 13:48:20 +08:00
|
|
|
static DenseMap<Key, VarListElementInit*> ThePool;
|
2011-07-30 03:07:23 +08:00
|
|
|
|
|
|
|
Key TheKey(std::make_pair(T, E));
|
|
|
|
|
2016-12-04 13:48:20 +08:00
|
|
|
VarListElementInit *&I = ThePool[TheKey];
|
|
|
|
if (!I) I = new(Allocator) VarListElementInit(T, E);
|
|
|
|
return I;
|
2011-07-30 03:07:07 +08:00
|
|
|
}
|
|
|
|
|
2007-11-23 05:05:25 +08:00
|
|
|
std::string VarListElementInit::getAsString() const {
|
|
|
|
return TI->getAsString() + "[" + utostr(Element) + "]";
|
|
|
|
}
|
|
|
|
|
2018-03-05 23:20:51 +08:00
|
|
|
Init *VarListElementInit::resolveReferences(Resolver &R) const {
|
|
|
|
Init *NewTI = TI->resolveReferences(R);
|
2018-02-26 04:50:04 +08:00
|
|
|
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());
|
2011-07-30 06:43:06 +08:00
|
|
|
return const_cast<VarListElementInit *>(this);
|
2004-07-27 09:01:21 +08:00
|
|
|
}
|
|
|
|
|
Re-work bit/bits value resolving in tblgen
- This patch is inspired by the failure of the following code snippet
which is used to convert enumerable values into encoding bits to
improve the readability of td files.
class S<int s> {
bits<2> V = !if(!eq(s, 8), {0, 0},
!if(!eq(s, 16), {0, 1},
!if(!eq(s, 32), {1, 0},
!if(!eq(s, 64), {1, 1}, {?, ?}))));
}
Later, PR8330 is found to report not exactly the same bug relevant
issue to bit/bits values.
- Instead of resolving bit/bits values separately through
resolveBitReference(), this patch adds getBit() for all Inits and
resolves bit value by resolving plus getting the specified bit. This
unifies the resolving of bit with other values and removes redundant
logic for resolving bit only. In addition,
BitsInit::resolveReferences() is optimized to take advantage of this
origanization by resolving VarBitInit's variable reference first and
then getting bits from it.
- The type interference in '!if' operator is revised to support possible
combinations of int and bits/bit in MHS and RHS.
- As there may be illegal assignments from integer value to bit, says
assign 2 to a bit, but we only check this during instantiation in some
cases, e.g.
bit V = !if(!eq(x, 17), 0, 2);
Verbose diagnostic message is generated when invalid value is
resolveed to help locating the error.
- PR8330 is fixed as well.
llvm-svn: 163360
2012-09-07 07:32:48 +08:00
|
|
|
Init *VarListElementInit::getBit(unsigned Bit) const {
|
|
|
|
if (getType() == BitRecTy::get())
|
|
|
|
return const_cast<VarListElementInit*>(this);
|
|
|
|
return VarBitInit::get(const_cast<VarListElementInit*>(this), Bit);
|
2004-07-27 09:01:21 +08:00
|
|
|
}
|
|
|
|
|
2018-03-06 21:48:20 +08:00
|
|
|
DefInit::DefInit(Record *D)
|
TableGen: Streamline how defs are instantiated
Summary:
Instantiating def's and defm's needs to perform the following steps:
- for defm's, clone multiclass def prototypes and subsitute template args
- for def's and defm's, add subclass definitions, substituting template
args
- clone the record based on foreach loops and substitute loop iteration
variables
- override record variables based on the global 'let' stack
- resolve the record name (this should be simple, but unfortunately it's
not due to existing .td files relying on rather silly implementation
details)
- for def(m)s in multiclasses, add the unresolved record as a multiclass
prototype
- for top-level def(m)s, resolve all internal variable references and add
them to the record keeper and any active defsets
This change streamlines how we go through these steps, by having both
def's and defm's feed into a single addDef() method that handles foreach,
final resolve, and routing the record to the right place.
This happens to make foreach inside of multiclasses work, as the new
test case demonstrates. Previously, foreach inside multiclasses was not
forbidden by the parser, but it was de facto broken.
Another side effect is that the order of "instantiated from" notes in error
messages is reversed, as the modified test case shows. This is arguably
clearer, since the initial error message ends up pointing directly to
whatever triggered the error, and subsequent notes will point to increasingly
outer layers of multiclasses. This is consistent with how C++ compilers
report nested #includes and nested template instantiations.
Change-Id: Ica146d0db2bc133dd7ed88054371becf24320447
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D44478
llvm-svn: 328117
2018-03-22 01:12:53 +08:00
|
|
|
: TypedInit(IK_DefInit, D->getType()), Def(D) {}
|
2018-03-06 21:48:20 +08:00
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
DefInit *DefInit::get(Record *R) {
|
2011-07-19 01:02:57 +08:00
|
|
|
return R->getDefInit();
|
|
|
|
}
|
|
|
|
|
2015-05-30 15:34:51 +08:00
|
|
|
Init *DefInit::convertInitializerTo(RecTy *Ty) const {
|
|
|
|
if (auto *RRT = dyn_cast<RecordRecTy>(Ty))
|
2018-03-06 21:48:20 +08:00
|
|
|
if (getType()->typeIsConvertibleTo(RRT))
|
2015-05-30 15:34:51 +08:00
|
|
|
return const_cast<DefInit *>(this);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-12-05 14:00:36 +08:00
|
|
|
RecTy *DefInit::getFieldType(StringInit *FieldName) const {
|
2003-10-06 03:27:59 +08:00
|
|
|
if (const RecordVal *RV = Def->getValue(FieldName))
|
|
|
|
return RV->getType();
|
2014-04-09 12:50:04 +08:00
|
|
|
return nullptr;
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
2020-01-29 03:23:46 +08:00
|
|
|
std::string DefInit::getAsString() const { return std::string(Def->getName()); }
|
2003-10-06 03:27:59 +08:00
|
|
|
|
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
2018-03-06 21:49:01 +08:00
|
|
|
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();
|
2019-08-15 23:54:37 +08:00
|
|
|
auto NewRecOwner = std::make_unique<Record>(Records.getNewAnonymousName(),
|
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
2018-03-06 21:49:01 +08:00
|
|
|
Class->getLoc(), Records,
|
|
|
|
/*IsAnonymous=*/true);
|
|
|
|
Record *NewRec = NewRecOwner.get();
|
|
|
|
|
|
|
|
// Copy values from class to instance
|
TableGen: Streamline the semantics of NAME
Summary:
The new rules are straightforward. The main rules to keep in mind
are:
1. NAME is an implicit template argument of class and multiclass,
and will be substituted by the name of the instantiating def/defm.
2. The name of a def/defm in a multiclass must contain a reference
to NAME. If such a reference is not present, it is automatically
prepended.
And for some additional subtleties, consider these:
3. defm with no name generates a unique name but has no special
behavior otherwise.
4. def with no name generates an anonymous record, whose name is
unique but undefined. In particular, the name won't contain a
reference to NAME.
Keeping rules 1&2 in mind should allow a predictable behavior of
name resolution that is simple to follow.
The old "rules" were rather surprising: sometimes (but not always),
NAME would correspond to the name of the toplevel defm. They were
also plain bonkers when you pushed them to their limits, as the old
version of the TableGen test case shows.
Having NAME correspond to the name of the toplevel defm introduces
"spooky action at a distance" and breaks composability:
refactoring the upper layers of a hierarchy of nested multiclass
instantiations can cause unexpected breakage by changing the value
of NAME at a lower level of the hierarchy. The new rules don't
suffer from this problem.
Some existing .td files have to be adjusted because they ended up
depending on the details of the old implementation.
Change-Id: I694095231565b30f563e6fd0417b41ee01a12589
Reviewers: tra, simon_tatham, craig.topper, MartinO, arsenm, javed.absar
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D47430
llvm-svn: 333900
2018-06-04 22:26:05 +08:00
|
|
|
for (const RecordVal &Val : Class->getValues())
|
|
|
|
NewRec->addValue(Val);
|
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
2018-03-06 21:49:01 +08:00
|
|
|
|
2021-03-23 01:38:38 +08:00
|
|
|
// Copy assertions from class to instance.
|
|
|
|
NewRec->appendAssertions(Class);
|
|
|
|
|
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
2018-03-06 21:49:01 +08:00
|
|
|
// 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));
|
|
|
|
|
2021-03-23 01:38:38 +08:00
|
|
|
// Check the assertions.
|
2021-04-02 01:01:43 +08:00
|
|
|
NewRec->checkRecordAssertions();
|
2021-03-23 01:38:38 +08:00
|
|
|
|
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
2018-03-06 21:49:01 +08:00
|
|
|
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 + ">";
|
|
|
|
}
|
|
|
|
|
2016-12-05 14:00:36 +08:00
|
|
|
FieldInit *FieldInit::get(Init *R, StringInit *FN) {
|
2017-06-16 08:43:26 +08:00
|
|
|
using Key = std::pair<Init *, StringInit *>;
|
2016-12-04 13:48:20 +08:00
|
|
|
static DenseMap<Key, FieldInit*> ThePool;
|
2011-07-30 03:07:24 +08:00
|
|
|
|
|
|
|
Key TheKey(std::make_pair(R, FN));
|
|
|
|
|
2016-12-04 13:48:20 +08:00
|
|
|
FieldInit *&I = ThePool[TheKey];
|
|
|
|
if (!I) I = new(Allocator) FieldInit(R, FN);
|
|
|
|
return I;
|
2011-07-30 03:07:07 +08:00
|
|
|
}
|
|
|
|
|
Re-work bit/bits value resolving in tblgen
- This patch is inspired by the failure of the following code snippet
which is used to convert enumerable values into encoding bits to
improve the readability of td files.
class S<int s> {
bits<2> V = !if(!eq(s, 8), {0, 0},
!if(!eq(s, 16), {0, 1},
!if(!eq(s, 32), {1, 0},
!if(!eq(s, 64), {1, 1}, {?, ?}))));
}
Later, PR8330 is found to report not exactly the same bug relevant
issue to bit/bits values.
- Instead of resolving bit/bits values separately through
resolveBitReference(), this patch adds getBit() for all Inits and
resolves bit value by resolving plus getting the specified bit. This
unifies the resolving of bit with other values and removes redundant
logic for resolving bit only. In addition,
BitsInit::resolveReferences() is optimized to take advantage of this
origanization by resolving VarBitInit's variable reference first and
then getting bits from it.
- The type interference in '!if' operator is revised to support possible
combinations of int and bits/bit in MHS and RHS.
- As there may be illegal assignments from integer value to bit, says
assign 2 to a bit, but we only check this during instantiation in some
cases, e.g.
bit V = !if(!eq(x, 17), 0, 2);
Verbose diagnostic message is generated when invalid value is
resolveed to help locating the error.
- PR8330 is fixed as well.
llvm-svn: 163360
2012-09-07 07:32:48 +08:00
|
|
|
Init *FieldInit::getBit(unsigned Bit) const {
|
|
|
|
if (getType() == BitRecTy::get())
|
|
|
|
return const_cast<FieldInit*>(this);
|
|
|
|
return VarBitInit::get(const_cast<FieldInit*>(this), Bit);
|
2004-07-27 09:01:21 +08:00
|
|
|
}
|
|
|
|
|
2018-03-05 23:20:51 +08:00
|
|
|
Init *FieldInit::resolveReferences(Resolver &R) const {
|
|
|
|
Init *NewRec = Rec->resolveReferences(R);
|
2018-03-09 20:24:30 +08:00
|
|
|
if (NewRec != Rec)
|
2018-03-19 22:14:28 +08:00
|
|
|
return FieldInit::get(NewRec, FieldName)->Fold(R.getCurrentRecord());
|
2018-03-09 20:24:30 +08:00
|
|
|
return const_cast<FieldInit *>(this);
|
|
|
|
}
|
2005-04-19 11:36:21 +08:00
|
|
|
|
2018-03-19 22:14:28 +08:00
|
|
|
Init *FieldInit::Fold(Record *CurRec) const {
|
2018-03-09 20:24:30 +08:00
|
|
|
if (DefInit *DI = dyn_cast<DefInit>(Rec)) {
|
2018-03-19 22:14:28 +08:00
|
|
|
Record *Def = DI->getDef();
|
|
|
|
if (Def == CurRec)
|
|
|
|
PrintFatalError(CurRec->getLoc(),
|
|
|
|
Twine("Attempting to access field '") +
|
|
|
|
FieldName->getAsUnquotedString() + "' of '" +
|
|
|
|
Rec->getAsString() + "' is a forbidden self-reference");
|
|
|
|
Init *FieldVal = Def->getValue(FieldName)->getValue();
|
2021-04-14 02:14:30 +08:00
|
|
|
if (FieldVal->isConcrete())
|
2018-03-09 20:24:30 +08:00
|
|
|
return FieldVal;
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
2011-07-30 06:43:06 +08:00
|
|
|
return const_cast<FieldInit *>(this);
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
2020-02-11 09:51:26 +08:00
|
|
|
bool FieldInit::isConcrete() const {
|
|
|
|
if (DefInit *DI = dyn_cast<DefInit>(Rec)) {
|
|
|
|
Init *FieldVal = DI->getDef()->getValue(FieldName)->getValue();
|
|
|
|
return FieldVal->isConcrete();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-01-25 18:25:25 +08:00
|
|
|
static void ProfileCondOpInit(FoldingSetNodeID &ID,
|
|
|
|
ArrayRef<Init *> CondRange,
|
|
|
|
ArrayRef<Init *> ValRange,
|
|
|
|
const RecTy *ValType) {
|
|
|
|
assert(CondRange.size() == ValRange.size() &&
|
|
|
|
"Number of conditions and values must match!");
|
|
|
|
ID.AddPointer(ValType);
|
|
|
|
ArrayRef<Init *>::iterator Case = CondRange.begin();
|
|
|
|
ArrayRef<Init *>::iterator Val = ValRange.begin();
|
|
|
|
|
|
|
|
while (Case != CondRange.end()) {
|
|
|
|
ID.AddPointer(*Case++);
|
|
|
|
ID.AddPointer(*Val++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CondOpInit::Profile(FoldingSetNodeID &ID) const {
|
|
|
|
ProfileCondOpInit(ID,
|
|
|
|
makeArrayRef(getTrailingObjects<Init *>(), NumConds),
|
|
|
|
makeArrayRef(getTrailingObjects<Init *>() + NumConds, NumConds),
|
|
|
|
ValType);
|
|
|
|
}
|
|
|
|
|
|
|
|
CondOpInit *
|
|
|
|
CondOpInit::get(ArrayRef<Init *> CondRange,
|
|
|
|
ArrayRef<Init *> ValRange, RecTy *Ty) {
|
|
|
|
assert(CondRange.size() == ValRange.size() &&
|
|
|
|
"Number of conditions and values must match!");
|
|
|
|
|
|
|
|
static FoldingSet<CondOpInit> ThePool;
|
|
|
|
FoldingSetNodeID ID;
|
|
|
|
ProfileCondOpInit(ID, CondRange, ValRange, Ty);
|
|
|
|
|
|
|
|
void *IP = nullptr;
|
|
|
|
if (CondOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP))
|
|
|
|
return I;
|
|
|
|
|
|
|
|
void *Mem = Allocator.Allocate(totalSizeToAlloc<Init *>(2*CondRange.size()),
|
|
|
|
alignof(BitsInit));
|
|
|
|
CondOpInit *I = new(Mem) CondOpInit(CondRange.size(), Ty);
|
|
|
|
|
|
|
|
std::uninitialized_copy(CondRange.begin(), CondRange.end(),
|
|
|
|
I->getTrailingObjects<Init *>());
|
|
|
|
std::uninitialized_copy(ValRange.begin(), ValRange.end(),
|
|
|
|
I->getTrailingObjects<Init *>()+CondRange.size());
|
|
|
|
ThePool.InsertNode(I, IP);
|
|
|
|
return I;
|
|
|
|
}
|
|
|
|
|
|
|
|
Init *CondOpInit::resolveReferences(Resolver &R) const {
|
|
|
|
SmallVector<Init*, 4> NewConds;
|
|
|
|
bool Changed = false;
|
|
|
|
for (const Init *Case : getConds()) {
|
|
|
|
Init *NewCase = Case->resolveReferences(R);
|
|
|
|
NewConds.push_back(NewCase);
|
|
|
|
Changed |= NewCase != Case;
|
|
|
|
}
|
|
|
|
|
|
|
|
SmallVector<Init*, 4> NewVals;
|
|
|
|
for (const Init *Val : getVals()) {
|
|
|
|
Init *NewVal = Val->resolveReferences(R);
|
|
|
|
NewVals.push_back(NewVal);
|
|
|
|
Changed |= NewVal != Val;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Changed)
|
|
|
|
return (CondOpInit::get(NewConds, NewVals,
|
|
|
|
getValType()))->Fold(R.getCurrentRecord());
|
|
|
|
|
|
|
|
return const_cast<CondOpInit *>(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
Init *CondOpInit::Fold(Record *CurRec) const {
|
|
|
|
for ( unsigned i = 0; i < NumConds; ++i) {
|
|
|
|
Init *Cond = getCond(i);
|
|
|
|
Init *Val = getVal(i);
|
|
|
|
|
|
|
|
if (IntInit *CondI = dyn_cast_or_null<IntInit>(
|
|
|
|
Cond->convertInitializerTo(IntRecTy::get()))) {
|
|
|
|
if (CondI->getValue())
|
|
|
|
return Val->convertInitializerTo(getValType());
|
|
|
|
} else
|
|
|
|
return const_cast<CondOpInit *>(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
PrintFatalError(CurRec->getLoc(),
|
|
|
|
CurRec->getName() +
|
|
|
|
" does not have any true condition in:" +
|
|
|
|
this->getAsString());
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CondOpInit::isConcrete() const {
|
|
|
|
for (const Init *Case : getConds())
|
|
|
|
if (!Case->isConcrete())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (const Init *Val : getVals())
|
|
|
|
if (!Val->isConcrete())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CondOpInit::isComplete() const {
|
|
|
|
for (const Init *Case : getConds())
|
|
|
|
if (!Case->isComplete())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (const Init *Val : getVals())
|
|
|
|
if (!Val->isConcrete())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string CondOpInit::getAsString() const {
|
|
|
|
std::string Result = "!cond(";
|
|
|
|
for (unsigned i = 0; i < getNumConds(); i++) {
|
|
|
|
Result += getCond(i)->getAsString() + ": ";
|
|
|
|
Result += getVal(i)->getAsString();
|
|
|
|
if (i != getNumConds()-1)
|
|
|
|
Result += ", ";
|
|
|
|
}
|
|
|
|
return Result + ")";
|
|
|
|
}
|
|
|
|
|
|
|
|
Init *CondOpInit::getBit(unsigned Bit) const {
|
|
|
|
return VarBitInit::get(const_cast<CondOpInit *>(this), Bit);
|
|
|
|
}
|
|
|
|
|
2016-12-05 14:00:41 +08:00
|
|
|
static void ProfileDagInit(FoldingSetNodeID &ID, Init *V, StringInit *VN,
|
2013-02-15 20:30:38 +08:00
|
|
|
ArrayRef<Init *> ArgRange,
|
2016-12-05 14:00:46 +08:00
|
|
|
ArrayRef<StringInit *> NameRange) {
|
2011-07-30 03:07:26 +08:00
|
|
|
ID.AddPointer(V);
|
2016-12-05 14:00:41 +08:00
|
|
|
ID.AddPointer(VN);
|
2011-07-30 03:07:26 +08:00
|
|
|
|
2016-12-05 14:00:46 +08:00
|
|
|
ArrayRef<Init *>::iterator Arg = ArgRange.begin();
|
|
|
|
ArrayRef<StringInit *>::iterator Name = NameRange.begin();
|
2011-07-30 03:07:26 +08:00
|
|
|
while (Arg != ArgRange.end()) {
|
|
|
|
assert(Name != NameRange.end() && "Arg name underflow!");
|
|
|
|
ID.AddPointer(*Arg++);
|
2016-12-05 14:00:46 +08:00
|
|
|
ID.AddPointer(*Name++);
|
2011-07-30 03:07:26 +08:00
|
|
|
}
|
|
|
|
assert(Name == NameRange.end() && "Arg name overflow!");
|
|
|
|
}
|
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
DagInit *
|
2016-12-05 14:00:41 +08:00
|
|
|
DagInit::get(Init *V, StringInit *VN, ArrayRef<Init *> ArgRange,
|
2016-12-05 14:00:46 +08:00
|
|
|
ArrayRef<StringInit *> NameRange) {
|
2015-04-24 13:38:48 +08:00
|
|
|
static FoldingSet<DagInit> ThePool;
|
2011-07-30 03:07:26 +08:00
|
|
|
|
|
|
|
FoldingSetNodeID ID;
|
|
|
|
ProfileDagInit(ID, V, VN, ArgRange, NameRange);
|
|
|
|
|
2014-04-09 12:50:04 +08:00
|
|
|
void *IP = nullptr;
|
2011-07-30 06:43:06 +08:00
|
|
|
if (DagInit *I = ThePool.FindNodeOrInsertPos(ID, IP))
|
2011-07-30 03:07:26 +08:00
|
|
|
return I;
|
|
|
|
|
2017-05-28 01:36:50 +08:00
|
|
|
void *Mem = Allocator.Allocate(totalSizeToAlloc<Init *, StringInit *>(ArgRange.size(), NameRange.size()), alignof(BitsInit));
|
|
|
|
DagInit *I = new(Mem) DagInit(V, VN, ArgRange.size(), NameRange.size());
|
|
|
|
std::uninitialized_copy(ArgRange.begin(), ArgRange.end(),
|
|
|
|
I->getTrailingObjects<Init *>());
|
|
|
|
std::uninitialized_copy(NameRange.begin(), NameRange.end(),
|
|
|
|
I->getTrailingObjects<StringInit *>());
|
2011-07-30 03:07:26 +08:00
|
|
|
ThePool.InsertNode(I, IP);
|
|
|
|
return I;
|
|
|
|
}
|
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
DagInit *
|
2016-12-05 14:00:41 +08:00
|
|
|
DagInit::get(Init *V, StringInit *VN,
|
2016-12-05 14:41:51 +08:00
|
|
|
ArrayRef<std::pair<Init*, StringInit*>> args) {
|
|
|
|
SmallVector<Init *, 8> Args;
|
|
|
|
SmallVector<StringInit *, 8> Names;
|
2011-07-30 03:07:07 +08:00
|
|
|
|
2015-04-29 15:13:14 +08:00
|
|
|
for (const auto &Arg : args) {
|
|
|
|
Args.push_back(Arg.first);
|
|
|
|
Names.push_back(Arg.second);
|
2011-07-30 03:07:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return DagInit::get(V, VN, Args, Names);
|
|
|
|
}
|
|
|
|
|
2011-07-30 03:07:26 +08:00
|
|
|
void DagInit::Profile(FoldingSetNodeID &ID) const {
|
2017-05-28 01:36:50 +08:00
|
|
|
ProfileDagInit(ID, Val, ValName, makeArrayRef(getTrailingObjects<Init *>(), NumArgs), makeArrayRef(getTrailingObjects<StringInit *>(), NumArgNames));
|
2011-07-30 03:07:07 +08:00
|
|
|
}
|
|
|
|
|
[tblgen] Add getOperatorAsDef() to Record
Summary:
While working with DagInit's, it's often the case that you expect the
operator to be a reference to a def. This patch adds a wrapper for this
common case to reduce the amount of boilerplate callers need to duplicate
repeatedly.
getOperatorAsDef() returns the record if the DagInit has an operator that is
a DefInit. Otherwise, it prints a fatal error.
There's only a few pre-existing examples in LLVM at the moment and I've
left a few instances of the code this simplifies as they had more specific
error messages than the generic one this produces. I'm going to be using
this a fair bit in my subsequent patches.
Reviewers: bogner, volkan, nhaehnle
Reviewed By: nhaehnle
Subscribers: nhaehnle, hiraditya, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, MaskRay, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, PkmX, jocewei, lenary, s.egerton, pzheng, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D68424
llvm-svn: 374101
2019-10-09 02:41:32 +08:00
|
|
|
Record *DagInit::getOperatorAsDef(ArrayRef<SMLoc> Loc) const {
|
|
|
|
if (DefInit *DefI = dyn_cast<DefInit>(Val))
|
|
|
|
return DefI->getDef();
|
|
|
|
PrintFatalError(Loc, "Expected record as operator");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-03-05 23:20:51 +08:00
|
|
|
Init *DagInit::resolveReferences(Resolver &R) const {
|
2016-12-05 14:41:51 +08:00
|
|
|
SmallVector<Init*, 8> NewArgs;
|
2017-05-28 01:36:50 +08:00
|
|
|
NewArgs.reserve(arg_size());
|
2016-12-05 14:41:51 +08:00
|
|
|
bool ArgsChanged = false;
|
2017-05-30 05:49:34 +08:00
|
|
|
for (const Init *Arg : getArgs()) {
|
2018-03-05 23:20:51 +08:00
|
|
|
Init *NewArg = Arg->resolveReferences(R);
|
2016-12-05 14:41:51 +08:00
|
|
|
NewArgs.push_back(NewArg);
|
|
|
|
ArgsChanged |= NewArg != Arg;
|
|
|
|
}
|
2009-11-22 12:24:42 +08:00
|
|
|
|
2018-03-05 23:20:51 +08:00
|
|
|
Init *Op = Val->resolveReferences(R);
|
2016-12-05 14:41:51 +08:00
|
|
|
if (Op != Val || ArgsChanged)
|
2017-05-28 01:36:50 +08:00
|
|
|
return DagInit::get(Op, ValName, NewArgs, getArgNames());
|
2009-11-22 12:24:42 +08:00
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
return const_cast<DagInit *>(this);
|
2006-01-31 14:02:35 +08:00
|
|
|
}
|
|
|
|
|
2018-03-06 21:48:47 +08:00
|
|
|
bool DagInit::isConcrete() const {
|
|
|
|
if (!Val->isConcrete())
|
|
|
|
return false;
|
|
|
|
for (const Init *Elt : getArgs()) {
|
|
|
|
if (!Elt->isConcrete())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-11-23 05:05:25 +08:00
|
|
|
std::string DagInit::getAsString() const {
|
|
|
|
std::string Result = "(" + Val->getAsString();
|
2016-12-05 14:00:41 +08:00
|
|
|
if (ValName)
|
|
|
|
Result += ":" + ValName->getAsUnquotedString();
|
2017-05-28 01:36:50 +08:00
|
|
|
if (!arg_empty()) {
|
|
|
|
Result += " " + getArg(0)->getAsString();
|
|
|
|
if (getArgName(0)) Result += ":$" + getArgName(0)->getAsUnquotedString();
|
|
|
|
for (unsigned i = 1, e = getNumArgs(); i != e; ++i) {
|
|
|
|
Result += ", " + getArg(i)->getAsString();
|
|
|
|
if (getArgName(i)) Result += ":$" + getArgName(i)->getAsUnquotedString();
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
}
|
2007-11-23 05:05:25 +08:00
|
|
|
return Result + ")";
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Other implementations
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2021-01-01 03:50:51 +08:00
|
|
|
RecordVal::RecordVal(Init *N, RecTy *T, FieldKind K)
|
|
|
|
: Name(N), TyAndKind(T, K) {
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
setValue(UnsetInit::get());
|
2003-10-06 03:27:59 +08:00
|
|
|
assert(Value && "Cannot create unset value for current type!");
|
|
|
|
}
|
|
|
|
|
2020-09-03 21:41:09 +08:00
|
|
|
// This constructor accepts the same arguments as the above, but also
|
|
|
|
// a source location.
|
2021-01-01 03:50:51 +08:00
|
|
|
RecordVal::RecordVal(Init *N, SMLoc Loc, RecTy *T, FieldKind K)
|
|
|
|
: Name(N), Loc(Loc), TyAndKind(T, K) {
|
2020-09-03 21:41:09 +08:00
|
|
|
setValue(UnsetInit::get());
|
|
|
|
assert(Value && "Cannot create unset value for current type!");
|
|
|
|
}
|
|
|
|
|
2016-12-04 13:48:16 +08:00
|
|
|
StringRef RecordVal::getName() const {
|
2015-06-04 15:40:12 +08:00
|
|
|
return cast<StringInit>(getNameInit())->getValue();
|
2011-09-03 04:12:07 +08:00
|
|
|
}
|
|
|
|
|
2020-11-25 02:09:02 +08:00
|
|
|
std::string RecordVal::getPrintType() const {
|
|
|
|
if (getType() == StringRecTy::get()) {
|
|
|
|
if (auto *StrInit = dyn_cast<StringInit>(Value)) {
|
|
|
|
if (StrInit->hasCodeFormat())
|
|
|
|
return "code";
|
|
|
|
else
|
|
|
|
return "string";
|
|
|
|
} else {
|
|
|
|
return "string";
|
|
|
|
}
|
|
|
|
} else {
|
2021-01-01 03:50:51 +08:00
|
|
|
return TyAndKind.getPointer()->getAsString();
|
2020-11-25 02:09:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
bool RecordVal::setValue(Init *V) {
|
|
|
|
if (V) {
|
|
|
|
Value = V->getCastTo(getType());
|
|
|
|
if (Value) {
|
|
|
|
assert(!isa<TypedInit>(Value) ||
|
|
|
|
cast<TypedInit>(Value)->getType()->typeIsA(getType()));
|
|
|
|
if (BitsRecTy *BTy = dyn_cast<BitsRecTy>(getType())) {
|
|
|
|
if (!isa<BitsInit>(Value)) {
|
|
|
|
SmallVector<Init *, 64> Bits;
|
|
|
|
Bits.reserve(BTy->getNumBits());
|
2020-09-03 21:41:09 +08:00
|
|
|
for (unsigned I = 0, E = BTy->getNumBits(); I < E; ++I)
|
|
|
|
Bits.push_back(Value->getBit(I));
|
|
|
|
Value = BitsInit::get(Bits);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Value == nullptr;
|
|
|
|
}
|
|
|
|
Value = nullptr;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-11-22 22:56:42 +08:00
|
|
|
// This version of setValue takes a source location and resets the
|
2020-09-03 21:41:09 +08:00
|
|
|
// location in the RecordVal.
|
|
|
|
bool RecordVal::setValue(Init *V, SMLoc NewLoc) {
|
|
|
|
Loc = NewLoc;
|
|
|
|
if (V) {
|
|
|
|
Value = V->getCastTo(getType());
|
|
|
|
if (Value) {
|
|
|
|
assert(!isa<TypedInit>(Value) ||
|
|
|
|
cast<TypedInit>(Value)->getType()->typeIsA(getType()));
|
|
|
|
if (BitsRecTy *BTy = dyn_cast<BitsRecTy>(getType())) {
|
|
|
|
if (!isa<BitsInit>(Value)) {
|
|
|
|
SmallVector<Init *, 64> Bits;
|
|
|
|
Bits.reserve(BTy->getNumBits());
|
|
|
|
for (unsigned I = 0, E = BTy->getNumBits(); I < E; ++I)
|
|
|
|
Bits.push_back(Value->getBit(I));
|
TableGen: Allow !cast of records, cleanup conversion machinery
Summary:
Distinguish two relationships between types: is-a and convertible-to.
For example, a bit is not an int or vice versa, but they can be
converted into each other (with range checks that you can think of
as "dynamic": unlike other type checks, those range checks do not
happen during parsing, but only once the final values have been
established).
Actually converting initializers between types is subtle: even
when values of type A can be converted to type B (e.g. int into
string), it may not be possible to do so with a concrete initializer
(e.g., a VarInit that refers to a variable of type int cannot
be immediately converted to a string).
For this reason, distinguish between getCastTo and convertInitializerTo:
the latter implements the actual conversion when appropriate, while
the former will first try to do the actual conversion and fall back
to introducing a !cast operation so that the conversion will be
delayed until variable references have been resolved.
To make the approach of adding !cast operations to work, !cast needs
to fallback to convertInitializerTo when the special string <-> record
logic does not apply.
This enables casting records to a subclass, although that new
functionality is only truly useful together with !isa, which will be
added in a later change.
The test is removed because it uses !srl on a bit sequence,
which cannot really be supported consistently, but luckily
isn't used anywhere either.
Change-Id: I98168bf52649176654ed2ec61a29bdb29970cfe7
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D43753
llvm-svn: 326785
2018-03-06 21:48:39 +08:00
|
|
|
Value = BitsInit::get(Bits);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Value == nullptr;
|
|
|
|
}
|
|
|
|
Value = nullptr;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-09-03 21:41:09 +08:00
|
|
|
#include "llvm/TableGen/Record.h"
|
2017-10-15 22:32:27 +08:00
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
2016-01-30 04:50:44 +08:00
|
|
|
LLVM_DUMP_METHOD void RecordVal::dump() const { errs() << *this; }
|
2017-01-28 10:02:38 +08:00
|
|
|
#endif
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2009-07-03 08:10:29 +08:00
|
|
|
void RecordVal::print(raw_ostream &OS, bool PrintSem) const {
|
2021-01-01 03:50:51 +08:00
|
|
|
if (isNonconcreteOK()) OS << "field ";
|
2020-11-25 02:09:02 +08:00
|
|
|
OS << getPrintType() << " " << getNameInitAsString();
|
2005-04-19 11:36:21 +08:00
|
|
|
|
|
|
|
if (getValue())
|
2003-10-06 03:27:59 +08:00
|
|
|
OS << " = " << *getValue();
|
2005-04-19 11:36:21 +08:00
|
|
|
|
2003-10-06 03:27:59 +08:00
|
|
|
if (PrintSem) OS << ";\n";
|
|
|
|
}
|
|
|
|
|
2009-08-23 17:47:37 +08:00
|
|
|
unsigned Record::LastID = 0;
|
|
|
|
|
2011-08-11 02:27:46 +08:00
|
|
|
void Record::checkName() {
|
|
|
|
// Ensure the record name has string type.
|
2015-04-29 15:13:05 +08:00
|
|
|
const TypedInit *TypedName = cast<const TypedInit>(Name);
|
2015-06-06 09:34:00 +08:00
|
|
|
if (!isa<StringRecTy>(TypedName->getType()))
|
TableGen: Streamline how defs are instantiated
Summary:
Instantiating def's and defm's needs to perform the following steps:
- for defm's, clone multiclass def prototypes and subsitute template args
- for def's and defm's, add subclass definitions, substituting template
args
- clone the record based on foreach loops and substitute loop iteration
variables
- override record variables based on the global 'let' stack
- resolve the record name (this should be simple, but unfortunately it's
not due to existing .td files relying on rather silly implementation
details)
- for def(m)s in multiclasses, add the unresolved record as a multiclass
prototype
- for top-level def(m)s, resolve all internal variable references and add
them to the record keeper and any active defsets
This change streamlines how we go through these steps, by having both
def's and defm's feed into a single addDef() method that handles foreach,
final resolve, and routing the record to the right place.
This happens to make foreach inside of multiclasses work, as the new
test case demonstrates. Previously, foreach inside multiclasses was not
forbidden by the parser, but it was de facto broken.
Another side effect is that the order of "instantiated from" notes in error
messages is reversed, as the modified test case shows. This is arguably
clearer, since the initial error message ends up pointing directly to
whatever triggered the error, and subsequent notes will point to increasingly
outer layers of multiclasses. This is consistent with how C++ compilers
report nested #includes and nested template instantiations.
Change-Id: Ica146d0db2bc133dd7ed88054371becf24320447
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D44478
llvm-svn: 328117
2018-03-22 01:12:53 +08:00
|
|
|
PrintFatalError(getLoc(), Twine("Record name '") + Name->getAsString() +
|
|
|
|
"' is not a string!");
|
|
|
|
}
|
|
|
|
|
|
|
|
RecordRecTy *Record::getType() {
|
|
|
|
SmallVector<Record *, 4> DirectSCs;
|
|
|
|
getDirectSuperClasses(DirectSCs);
|
|
|
|
return RecordRecTy::get(DirectSCs);
|
2011-08-11 02:27:46 +08:00
|
|
|
}
|
|
|
|
|
2011-07-19 01:02:57 +08:00
|
|
|
DefInit *Record::getDefInit() {
|
2020-09-19 01:54:38 +08:00
|
|
|
if (!CorrespondingDefInit)
|
|
|
|
CorrespondingDefInit = new (Allocator) DefInit(this);
|
|
|
|
return CorrespondingDefInit;
|
2011-07-19 01:02:57 +08:00
|
|
|
}
|
|
|
|
|
2011-08-11 02:27:46 +08:00
|
|
|
void Record::setName(Init *NewName) {
|
2011-10-19 21:03:25 +08:00
|
|
|
Name = NewName;
|
2011-08-11 02:27:46 +08:00
|
|
|
checkName();
|
|
|
|
// DO NOT resolve record values to the name at this point because
|
|
|
|
// there might be default values for arguments of this def. Those
|
|
|
|
// arguments might not have been resolved yet so we don't want to
|
|
|
|
// prematurely assume values for those arguments were not passed to
|
|
|
|
// this def.
|
|
|
|
//
|
|
|
|
// Nonetheless, it may be that some of this Record's values
|
|
|
|
// reference the record name. Indeed, the reason for having the
|
|
|
|
// record name be an Init is to provide this flexibility. The extra
|
|
|
|
// resolve steps after completely instantiating defs takes care of
|
|
|
|
// this. See TGParser::ParseDef and TGParser::ParseDefm.
|
|
|
|
}
|
|
|
|
|
2020-09-23 01:58:54 +08:00
|
|
|
// NOTE for the next two functions:
|
|
|
|
// Superclasses are in post-order, so the final one is a direct
|
|
|
|
// superclass. All of its transitive superclases immediately precede it,
|
|
|
|
// so we can step through the direct superclasses in reverse order.
|
|
|
|
|
|
|
|
bool Record::hasDirectSuperClass(const Record *Superclass) const {
|
|
|
|
ArrayRef<std::pair<Record *, SMRange>> SCs = getSuperClasses();
|
|
|
|
|
|
|
|
for (int I = SCs.size() - 1; I >= 0; --I) {
|
|
|
|
const Record *SC = SCs[I].first;
|
|
|
|
if (SC == Superclass)
|
|
|
|
return true;
|
|
|
|
I -= SC->getSuperClasses().size();
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-03-06 21:48:20 +08:00
|
|
|
void Record::getDirectSuperClasses(SmallVectorImpl<Record *> &Classes) const {
|
|
|
|
ArrayRef<std::pair<Record *, SMRange>> SCs = getSuperClasses();
|
2020-09-20 00:22:12 +08:00
|
|
|
|
2018-03-06 21:48:20 +08:00
|
|
|
while (!SCs.empty()) {
|
|
|
|
Record *SC = SCs.back().first;
|
|
|
|
SCs = SCs.drop_back(1 + SC->getSuperClasses().size());
|
|
|
|
Classes.push_back(SC);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 23:21:11 +08:00
|
|
|
void Record::resolveReferences(Resolver &R, const RecordVal *SkipVal) {
|
2021-02-01 23:59:07 +08:00
|
|
|
Init *OldName = getNameInit();
|
|
|
|
Init *NewName = Name->resolveReferences(R);
|
|
|
|
if (NewName != OldName) {
|
|
|
|
// Re-register with RecordKeeper.
|
|
|
|
setName(NewName);
|
|
|
|
}
|
2021-03-23 01:38:38 +08:00
|
|
|
|
|
|
|
// Resolve the field values.
|
2016-12-05 15:00:44 +08:00
|
|
|
for (RecordVal &Value : Values) {
|
2018-03-05 23:21:11 +08:00
|
|
|
if (SkipVal == &Value) // Skip resolve the same field as the given one
|
2012-03-08 00:39:35 +08:00
|
|
|
continue;
|
2018-03-05 23:21:11 +08:00
|
|
|
if (Init *V = Value.getValue()) {
|
|
|
|
Init *VR = V->resolveReferences(R);
|
2018-03-09 20:24:14 +08:00
|
|
|
if (Value.setValue(VR)) {
|
|
|
|
std::string Type;
|
|
|
|
if (TypedInit *VRT = dyn_cast<TypedInit>(VR))
|
|
|
|
Type =
|
|
|
|
(Twine("of type '") + VRT->getType()->getAsString() + "' ").str();
|
2021-02-26 05:33:08 +08:00
|
|
|
PrintFatalError(
|
|
|
|
getLoc(),
|
|
|
|
Twine("Invalid value ") + Type + "found when setting field '" +
|
|
|
|
Value.getNameInitAsString() + "' of type '" +
|
|
|
|
Value.getType()->getAsString() +
|
|
|
|
"' after resolving references: " + VR->getAsUnquotedString() +
|
|
|
|
"\n");
|
2018-03-09 20:24:14 +08:00
|
|
|
}
|
2018-03-05 23:21:11 +08:00
|
|
|
}
|
2005-04-19 11:36:21 +08:00
|
|
|
}
|
2021-03-23 01:38:38 +08:00
|
|
|
|
|
|
|
// Resolve the assertion expressions.
|
|
|
|
for (auto &Assertion : Assertions) {
|
2021-04-20 22:00:24 +08:00
|
|
|
Init *Value = Assertion.Condition->resolveReferences(R);
|
|
|
|
Assertion.Condition = Value;
|
|
|
|
Value = Assertion.Message->resolveReferences(R);
|
|
|
|
Assertion.Message = Value;
|
2021-03-23 01:38:38 +08:00
|
|
|
}
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
2021-02-01 23:59:07 +08:00
|
|
|
void Record::resolveReferences(Init *NewName) {
|
2018-03-05 23:21:11 +08:00
|
|
|
RecordResolver R(*this);
|
2021-02-01 23:59:07 +08:00
|
|
|
R.setName(NewName);
|
2018-03-19 22:14:20 +08:00
|
|
|
R.setFinal(true);
|
2018-03-05 23:21:11 +08:00
|
|
|
resolveReferences(R);
|
|
|
|
}
|
|
|
|
|
2017-10-15 22:32:27 +08:00
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
2016-01-30 04:50:44 +08:00
|
|
|
LLVM_DUMP_METHOD void Record::dump() const { errs() << *this; }
|
2017-01-28 10:02:38 +08:00
|
|
|
#endif
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2009-07-03 08:10:29 +08:00
|
|
|
raw_ostream &llvm::operator<<(raw_ostream &OS, const Record &R) {
|
2011-10-19 21:02:57 +08:00
|
|
|
OS << R.getNameInitAsString();
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2015-10-24 20:46:45 +08:00
|
|
|
ArrayRef<Init *> TArgs = R.getTemplateArgs();
|
2003-10-06 03:27:59 +08:00
|
|
|
if (!TArgs.empty()) {
|
|
|
|
OS << "<";
|
2015-06-04 15:40:14 +08:00
|
|
|
bool NeedComma = false;
|
|
|
|
for (const Init *TA : TArgs) {
|
|
|
|
if (NeedComma) OS << ", ";
|
|
|
|
NeedComma = true;
|
|
|
|
const RecordVal *RV = R.getValue(TA);
|
2003-10-06 03:27:59 +08:00
|
|
|
assert(RV && "Template argument record not found??");
|
|
|
|
RV->print(OS, false);
|
|
|
|
}
|
|
|
|
OS << ">";
|
|
|
|
}
|
|
|
|
|
|
|
|
OS << " {";
|
2016-01-19 03:52:37 +08:00
|
|
|
ArrayRef<std::pair<Record *, SMRange>> SC = R.getSuperClasses();
|
2003-10-06 03:27:59 +08:00
|
|
|
if (!SC.empty()) {
|
|
|
|
OS << "\t//";
|
2016-01-19 03:52:37 +08:00
|
|
|
for (const auto &SuperPair : SC)
|
|
|
|
OS << " " << SuperPair.first->getNameInitAsString();
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
OS << "\n";
|
|
|
|
|
2015-06-04 15:40:14 +08:00
|
|
|
for (const RecordVal &Val : R.getValues())
|
2021-01-01 03:50:51 +08:00
|
|
|
if (Val.isNonconcreteOK() && !R.isTemplateArg(Val.getNameInit()))
|
2015-06-04 15:40:14 +08:00
|
|
|
OS << Val;
|
|
|
|
for (const RecordVal &Val : R.getValues())
|
2021-01-01 03:50:51 +08:00
|
|
|
if (!Val.isNonconcreteOK() && !R.isTemplateArg(Val.getNameInit()))
|
2015-06-04 15:40:14 +08:00
|
|
|
OS << Val;
|
2003-10-06 03:27:59 +08:00
|
|
|
|
|
|
|
return OS << "}\n";
|
|
|
|
}
|
|
|
|
|
2020-11-22 22:56:42 +08:00
|
|
|
SMLoc Record::getFieldLoc(StringRef FieldName) const {
|
|
|
|
const RecordVal *R = getValue(FieldName);
|
|
|
|
if (!R)
|
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() +
|
|
|
|
"' does not have a field named `" + FieldName + "'!\n");
|
|
|
|
return R->getLoc();
|
|
|
|
}
|
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
Init *Record::getValueInit(StringRef FieldName) const {
|
2003-10-06 03:27:59 +08:00
|
|
|
const RecordVal *R = getValue(FieldName);
|
2014-04-09 12:50:04 +08:00
|
|
|
if (!R || !R->getValue())
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() +
|
2014-03-30 01:17:15 +08:00
|
|
|
"' does not have a field named `" + FieldName + "'!\n");
|
2003-10-06 03:27:59 +08:00
|
|
|
return R->getValue();
|
|
|
|
}
|
|
|
|
|
2017-06-01 03:01:11 +08:00
|
|
|
StringRef Record::getValueAsString(StringRef FieldName) const {
|
2020-09-26 10:18:54 +08:00
|
|
|
llvm::Optional<StringRef> S = getValueAsOptionalString(FieldName);
|
|
|
|
if (!S.hasValue())
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() +
|
2014-03-30 01:17:15 +08:00
|
|
|
"' does not have a field named `" + FieldName + "'!\n");
|
2020-09-26 10:18:54 +08:00
|
|
|
return S.getValue();
|
|
|
|
}
|
2020-11-25 02:09:02 +08:00
|
|
|
|
2020-09-26 10:18:54 +08:00
|
|
|
llvm::Optional<StringRef>
|
|
|
|
Record::getValueAsOptionalString(StringRef FieldName) const {
|
|
|
|
const RecordVal *R = getValue(FieldName);
|
|
|
|
if (!R || !R->getValue())
|
|
|
|
return llvm::Optional<StringRef>();
|
|
|
|
if (isa<UnsetInit>(R->getValue()))
|
|
|
|
return llvm::Optional<StringRef>();
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2012-10-11 04:24:43 +08:00
|
|
|
if (StringInit *SI = dyn_cast<StringInit>(R->getValue()))
|
2003-10-06 03:27:59 +08:00
|
|
|
return SI->getValue();
|
2016-07-06 05:22:55 +08:00
|
|
|
|
2020-09-26 10:18:54 +08:00
|
|
|
PrintFatalError(getLoc(),
|
|
|
|
"Record `" + getName() + "', ` field `" + FieldName +
|
|
|
|
"' exists but does not have a string initializer!");
|
|
|
|
}
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
BitsInit *Record::getValueAsBitsInit(StringRef FieldName) const {
|
2003-10-06 03:27:59 +08:00
|
|
|
const RecordVal *R = getValue(FieldName);
|
2014-04-09 12:50:04 +08:00
|
|
|
if (!R || !R->getValue())
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() +
|
2014-03-30 01:17:15 +08:00
|
|
|
"' does not have a field named `" + FieldName + "'!\n");
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2012-10-11 04:24:43 +08:00
|
|
|
if (BitsInit *BI = dyn_cast<BitsInit>(R->getValue()))
|
2003-10-06 03:27:59 +08:00
|
|
|
return BI;
|
2020-09-03 21:41:09 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + FieldName +
|
|
|
|
"' exists but does not have a bits value");
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
ListInit *Record::getValueAsListInit(StringRef FieldName) const {
|
2003-10-06 03:27:59 +08:00
|
|
|
const RecordVal *R = getValue(FieldName);
|
2014-04-09 12:50:04 +08:00
|
|
|
if (!R || !R->getValue())
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() +
|
2014-03-30 01:17:15 +08:00
|
|
|
"' does not have a field named `" + FieldName + "'!\n");
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2012-10-11 04:24:43 +08:00
|
|
|
if (ListInit *LI = dyn_cast<ListInit>(R->getValue()))
|
2003-10-06 03:27:59 +08:00
|
|
|
return LI;
|
2020-09-03 21:41:09 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + FieldName +
|
|
|
|
"' exists but does not have a list value");
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
2009-11-22 12:24:42 +08:00
|
|
|
std::vector<Record*>
|
2009-09-19 02:31:37 +08:00
|
|
|
Record::getValueAsListOfDefs(StringRef FieldName) const {
|
2011-07-30 06:43:06 +08:00
|
|
|
ListInit *List = getValueAsListInit(FieldName);
|
2005-10-29 05:46:31 +08:00
|
|
|
std::vector<Record*> Defs;
|
2015-06-02 12:15:51 +08:00
|
|
|
for (Init *I : List->getValues()) {
|
|
|
|
if (DefInit *DI = dyn_cast<DefInit>(I))
|
2005-10-29 05:46:31 +08:00
|
|
|
Defs.push_back(DI->getDef());
|
2015-05-04 09:35:39 +08:00
|
|
|
else
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" +
|
2014-03-30 01:17:15 +08:00
|
|
|
FieldName + "' list is not entirely DefInit!");
|
2005-10-29 05:46:31 +08:00
|
|
|
}
|
|
|
|
return Defs;
|
|
|
|
}
|
|
|
|
|
2009-09-19 02:31:37 +08:00
|
|
|
int64_t Record::getValueAsInt(StringRef FieldName) const {
|
2003-10-06 03:27:59 +08:00
|
|
|
const RecordVal *R = getValue(FieldName);
|
2014-04-09 12:50:04 +08:00
|
|
|
if (!R || !R->getValue())
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() +
|
2014-03-30 01:17:15 +08:00
|
|
|
"' does not have a field named `" + FieldName + "'!\n");
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2012-10-11 04:24:43 +08:00
|
|
|
if (IntInit *II = dyn_cast<IntInit>(R->getValue()))
|
2003-10-06 03:27:59 +08:00
|
|
|
return II->getValue();
|
2018-03-09 20:24:14 +08:00
|
|
|
PrintFatalError(getLoc(), Twine("Record `") + getName() + "', field `" +
|
|
|
|
FieldName +
|
2020-09-03 21:41:09 +08:00
|
|
|
"' exists but does not have an int value: " +
|
2018-03-09 20:24:14 +08:00
|
|
|
R->getValue()->getAsString());
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
2009-11-22 12:24:42 +08:00
|
|
|
std::vector<int64_t>
|
2009-09-19 02:31:37 +08:00
|
|
|
Record::getValueAsListOfInts(StringRef FieldName) const {
|
2011-07-30 06:43:06 +08:00
|
|
|
ListInit *List = getValueAsListInit(FieldName);
|
2008-10-17 09:33:43 +08:00
|
|
|
std::vector<int64_t> Ints;
|
2015-06-02 12:15:51 +08:00
|
|
|
for (Init *I : List->getValues()) {
|
|
|
|
if (IntInit *II = dyn_cast<IntInit>(I))
|
2007-11-11 19:19:37 +08:00
|
|
|
Ints.push_back(II->getValue());
|
2015-05-04 09:35:39 +08:00
|
|
|
else
|
2018-03-09 20:24:14 +08:00
|
|
|
PrintFatalError(getLoc(),
|
|
|
|
Twine("Record `") + getName() + "', field `" + FieldName +
|
2020-09-03 21:41:09 +08:00
|
|
|
"' exists but does not have a list of ints value: " +
|
2018-03-09 20:24:14 +08:00
|
|
|
I->getAsString());
|
2007-11-11 19:19:37 +08:00
|
|
|
}
|
|
|
|
return Ints;
|
|
|
|
}
|
|
|
|
|
2017-06-01 03:01:11 +08:00
|
|
|
std::vector<StringRef>
|
2011-06-28 05:06:21 +08:00
|
|
|
Record::getValueAsListOfStrings(StringRef FieldName) const {
|
2011-07-30 06:43:06 +08:00
|
|
|
ListInit *List = getValueAsListInit(FieldName);
|
2017-06-01 03:01:11 +08:00
|
|
|
std::vector<StringRef> Strings;
|
2015-06-02 12:15:51 +08:00
|
|
|
for (Init *I : List->getValues()) {
|
|
|
|
if (StringInit *SI = dyn_cast<StringInit>(I))
|
|
|
|
Strings.push_back(SI->getValue());
|
2015-05-04 09:35:39 +08:00
|
|
|
else
|
2018-03-09 20:24:14 +08:00
|
|
|
PrintFatalError(getLoc(),
|
|
|
|
Twine("Record `") + getName() + "', field `" + FieldName +
|
2020-09-03 21:41:09 +08:00
|
|
|
"' exists but does not have a list of strings value: " +
|
2018-03-09 20:24:14 +08:00
|
|
|
I->getAsString());
|
2011-06-28 05:06:21 +08:00
|
|
|
}
|
|
|
|
return Strings;
|
|
|
|
}
|
|
|
|
|
2009-09-19 02:31:37 +08:00
|
|
|
Record *Record::getValueAsDef(StringRef FieldName) const {
|
2003-10-06 03:27:59 +08:00
|
|
|
const RecordVal *R = getValue(FieldName);
|
2014-04-09 12:50:04 +08:00
|
|
|
if (!R || !R->getValue())
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() +
|
2014-03-30 01:17:15 +08:00
|
|
|
"' does not have a field named `" + FieldName + "'!\n");
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2012-10-11 04:24:43 +08:00
|
|
|
if (DefInit *DI = dyn_cast<DefInit>(R->getValue()))
|
2003-10-06 03:27:59 +08:00
|
|
|
return DI->getDef();
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" +
|
2014-03-30 01:17:15 +08:00
|
|
|
FieldName + "' does not have a def initializer!");
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
2019-10-26 07:23:17 +08:00
|
|
|
Record *Record::getValueAsOptionalDef(StringRef FieldName) const {
|
|
|
|
const RecordVal *R = getValue(FieldName);
|
|
|
|
if (!R || !R->getValue())
|
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() +
|
|
|
|
"' does not have a field named `" + FieldName + "'!\n");
|
|
|
|
|
|
|
|
if (DefInit *DI = dyn_cast<DefInit>(R->getValue()))
|
|
|
|
return DI->getDef();
|
|
|
|
if (isa<UnsetInit>(R->getValue()))
|
|
|
|
return nullptr;
|
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" +
|
|
|
|
FieldName + "' does not have either a def initializer or '?'!");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-09-19 02:31:37 +08:00
|
|
|
bool Record::getValueAsBit(StringRef FieldName) const {
|
2003-10-06 03:27:59 +08:00
|
|
|
const RecordVal *R = getValue(FieldName);
|
2014-04-09 12:50:04 +08:00
|
|
|
if (!R || !R->getValue())
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() +
|
2014-03-30 01:17:15 +08:00
|
|
|
"' does not have a field named `" + FieldName + "'!\n");
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2012-10-11 04:24:43 +08:00
|
|
|
if (BitInit *BI = dyn_cast<BitInit>(R->getValue()))
|
2003-10-06 03:27:59 +08:00
|
|
|
return BI->getValue();
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" +
|
2014-03-30 01:17:15 +08:00
|
|
|
FieldName + "' does not have a bit initializer!");
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
2012-08-24 03:34:46 +08:00
|
|
|
bool Record::getValueAsBitOrUnset(StringRef FieldName, bool &Unset) const {
|
|
|
|
const RecordVal *R = getValue(FieldName);
|
2014-04-09 12:50:04 +08:00
|
|
|
if (!R || !R->getValue())
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() +
|
|
|
|
"' does not have a field named `" + FieldName.str() + "'!\n");
|
2012-08-24 03:34:46 +08:00
|
|
|
|
2015-04-22 10:09:45 +08:00
|
|
|
if (isa<UnsetInit>(R->getValue())) {
|
2012-08-24 03:34:46 +08:00
|
|
|
Unset = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Unset = false;
|
2012-10-11 04:24:43 +08:00
|
|
|
if (BitInit *BI = dyn_cast<BitInit>(R->getValue()))
|
2012-08-24 03:34:46 +08:00
|
|
|
return BI->getValue();
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" +
|
2014-03-30 01:17:15 +08:00
|
|
|
FieldName + "' does not have a bit initializer!");
|
2012-08-24 03:34:46 +08:00
|
|
|
}
|
|
|
|
|
2011-07-30 06:43:06 +08:00
|
|
|
DagInit *Record::getValueAsDag(StringRef FieldName) const {
|
2003-10-06 03:27:59 +08:00
|
|
|
const RecordVal *R = getValue(FieldName);
|
2014-04-09 12:50:04 +08:00
|
|
|
if (!R || !R->getValue())
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() +
|
2014-03-30 01:17:15 +08:00
|
|
|
"' does not have a field named `" + FieldName + "'!\n");
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2012-10-11 04:24:43 +08:00
|
|
|
if (DagInit *DI = dyn_cast<DagInit>(R->getValue()))
|
2003-10-06 03:27:59 +08:00
|
|
|
return DI;
|
2012-10-26 04:33:17 +08:00
|
|
|
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" +
|
2014-03-30 01:17:15 +08:00
|
|
|
FieldName + "' does not have a dag initializer!");
|
2003-10-06 03:27:59 +08:00
|
|
|
}
|
|
|
|
|
2021-03-23 01:38:38 +08:00
|
|
|
// Check all record assertions: For each one, resolve the condition
|
|
|
|
// and message, then call CheckAssert().
|
|
|
|
// Note: The condition and message are probably already resolved,
|
|
|
|
// but resolving again allows calls before records are resolved.
|
2021-04-02 01:01:43 +08:00
|
|
|
void Record::checkRecordAssertions() {
|
2021-03-23 01:38:38 +08:00
|
|
|
RecordResolver R(*this);
|
|
|
|
R.setFinal(true);
|
|
|
|
|
2021-09-17 01:46:08 +08:00
|
|
|
for (const auto &Assertion : getAssertions()) {
|
2021-04-20 22:00:24 +08:00
|
|
|
Init *Condition = Assertion.Condition->resolveReferences(R);
|
|
|
|
Init *Message = Assertion.Message->resolveReferences(R);
|
|
|
|
CheckAssert(Assertion.Loc, Condition, Message);
|
2021-03-23 01:38:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-15 22:32:27 +08:00
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
2016-01-30 04:50:44 +08:00
|
|
|
LLVM_DUMP_METHOD void RecordKeeper::dump() const { errs() << *this; }
|
2017-01-28 10:02:38 +08:00
|
|
|
#endif
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2009-07-03 08:10:29 +08:00
|
|
|
raw_ostream &llvm::operator<<(raw_ostream &OS, const RecordKeeper &RK) {
|
2003-10-06 03:27:59 +08:00
|
|
|
OS << "------------- Classes -----------------\n";
|
2015-05-12 13:25:10 +08:00
|
|
|
for (const auto &C : RK.getClasses())
|
2014-08-25 03:10:49 +08:00
|
|
|
OS << "class " << *C.second;
|
2005-04-22 08:00:37 +08:00
|
|
|
|
2003-10-06 03:27:59 +08:00
|
|
|
OS << "------------- Defs -----------------\n";
|
2015-05-12 13:25:10 +08:00
|
|
|
for (const auto &D : RK.getDefs())
|
2014-08-25 03:10:49 +08:00
|
|
|
OS << "def " << *D.second;
|
2003-10-06 03:27:59 +08:00
|
|
|
return OS;
|
|
|
|
}
|
|
|
|
|
2018-03-06 21:48:54 +08:00
|
|
|
/// GetNewAnonymousName - Generate a unique anonymous name that can be used as
|
|
|
|
/// an identifier.
|
|
|
|
Init *RecordKeeper::getNewAnonymousName() {
|
2021-02-01 23:59:07 +08:00
|
|
|
return AnonymousNameInit::get(AnonCounter++);
|
2018-03-06 21:48:54 +08:00
|
|
|
}
|
|
|
|
|
2020-10-22 14:58:49 +08:00
|
|
|
// These functions implement the phase timing facility. Starting a timer
|
|
|
|
// when one is already running stops the running one.
|
|
|
|
|
|
|
|
void RecordKeeper::startTimer(StringRef Name) {
|
|
|
|
if (TimingGroup) {
|
|
|
|
if (LastTimer && LastTimer->isRunning()) {
|
|
|
|
LastTimer->stopTimer();
|
|
|
|
if (BackendTimer) {
|
|
|
|
LastTimer->clear();
|
|
|
|
BackendTimer = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LastTimer = new Timer("", Name, *TimingGroup);
|
|
|
|
LastTimer->startTimer();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RecordKeeper::stopTimer() {
|
|
|
|
if (TimingGroup) {
|
|
|
|
assert(LastTimer && "No phase timer was started");
|
|
|
|
LastTimer->stopTimer();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RecordKeeper::startBackendTimer(StringRef Name) {
|
|
|
|
if (TimingGroup) {
|
|
|
|
startTimer(Name);
|
|
|
|
BackendTimer = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RecordKeeper::stopBackendTimer() {
|
|
|
|
if (TimingGroup) {
|
2020-11-15 01:37:48 +08:00
|
|
|
if (BackendTimer) {
|
2020-10-22 14:58:49 +08:00
|
|
|
stopTimer();
|
|
|
|
BackendTimer = false;
|
2020-11-15 01:37:48 +08:00
|
|
|
}
|
2020-10-22 14:58:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-03 22:58:37 +08:00
|
|
|
// We cache the record vectors for single classes. Many backends request
|
|
|
|
// the same vectors multiple times.
|
2020-10-05 02:48:44 +08:00
|
|
|
std::vector<Record *> RecordKeeper::getAllDerivedDefinitions(
|
2020-12-03 22:58:37 +08:00
|
|
|
StringRef ClassName) const {
|
|
|
|
|
|
|
|
auto Pair = ClassRecordsMap.try_emplace(ClassName);
|
|
|
|
if (Pair.second)
|
|
|
|
Pair.first->second = getAllDerivedDefinitions(makeArrayRef(ClassName));
|
|
|
|
|
|
|
|
return Pair.first->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<Record *> RecordKeeper::getAllDerivedDefinitions(
|
|
|
|
ArrayRef<StringRef> ClassNames) const {
|
2020-10-05 02:48:44 +08:00
|
|
|
SmallVector<Record *, 2> ClassRecs;
|
|
|
|
std::vector<Record *> Defs;
|
2003-10-06 03:27:59 +08:00
|
|
|
|
2020-10-05 02:48:44 +08:00
|
|
|
assert(ClassNames.size() > 0 && "At least one class must be passed.");
|
|
|
|
for (const auto &ClassName : ClassNames) {
|
|
|
|
Record *Class = getClass(ClassName);
|
|
|
|
if (!Class)
|
|
|
|
PrintFatalError("The class '" + ClassName + "' is not defined\n");
|
|
|
|
ClassRecs.push_back(Class);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto &OneDef : getDefs()) {
|
|
|
|
if (all_of(ClassRecs, [&OneDef](const Record *Class) {
|
|
|
|
return OneDef.second->isSubClassOf(Class);
|
|
|
|
}))
|
|
|
|
Defs.push_back(OneDef.second.get());
|
|
|
|
}
|
2003-10-06 03:27:59 +08:00
|
|
|
|
|
|
|
return Defs;
|
|
|
|
}
|
2003-11-12 06:41:34 +08:00
|
|
|
|
TableGen: Reimplement !foreach using the resolving mechanism
Summary:
This changes the syntax of !foreach so that the first "parameter" is
a new syntactic variable: !foreach(x, lst, expr) will define the
variable x within the scope of expr, and evaluation of the !foreach
will substitute elements of the given list (or dag) for x in expr.
Aside from leading to a nicer syntax, this allows more complex
expressions where x is deeply nested, or even constant expressions
in which x does not occur at all.
!foreach is currently not actually used anywhere in trunk, but I
plan to use it in the AMDGPU backend. If out-of-tree targets are
using it, they can adjust to the new syntax very easily.
Change-Id: Ib966694d8ab6542279d6bc358b6f4d767945a805
Reviewers: arsenm, craig.topper, tra, MartinO
Subscribers: wdng, llvm-commits, tpr
Differential Revision: https://reviews.llvm.org/D43651
llvm-svn: 326705
2018-03-05 23:21:04 +08:00
|
|
|
Init *MapResolver::resolve(Init *VarName) {
|
|
|
|
auto It = Map.find(VarName);
|
|
|
|
if (It == Map.end())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
Init *I = It->second.V;
|
|
|
|
|
|
|
|
if (!It->second.Resolved && Map.size() > 1) {
|
|
|
|
// Resolve mutual references among the mapped variables, but prevent
|
|
|
|
// infinite recursion.
|
|
|
|
Map.erase(It);
|
|
|
|
I = I->resolveReferences(*this);
|
|
|
|
Map[VarName] = {I, true};
|
|
|
|
}
|
|
|
|
|
|
|
|
return I;
|
|
|
|
}
|
|
|
|
|
2018-03-05 23:20:51 +08:00
|
|
|
Init *RecordResolver::resolve(Init *VarName) {
|
|
|
|
Init *Val = Cache.lookup(VarName);
|
|
|
|
if (Val)
|
|
|
|
return Val;
|
|
|
|
|
2021-02-15 00:36:20 +08:00
|
|
|
if (llvm::is_contained(Stack, VarName))
|
|
|
|
return nullptr; // prevent infinite recursion
|
2018-03-05 23:20:51 +08:00
|
|
|
|
|
|
|
if (RecordVal *RV = getCurrentRecord()->getValue(VarName)) {
|
|
|
|
if (!isa<UnsetInit>(RV->getValue())) {
|
|
|
|
Val = RV->getValue();
|
|
|
|
Stack.push_back(VarName);
|
|
|
|
Val = Val->resolveReferences(*this);
|
|
|
|
Stack.pop_back();
|
|
|
|
}
|
2021-02-01 23:59:07 +08:00
|
|
|
} else if (Name && VarName == getCurrentRecord()->getNameInit()) {
|
|
|
|
Stack.push_back(VarName);
|
|
|
|
Val = Name->resolveReferences(*this);
|
|
|
|
Stack.pop_back();
|
2018-03-05 23:20:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Cache[VarName] = Val;
|
|
|
|
return Val;
|
|
|
|
}
|
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
2018-03-06 21:49:01 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
TableGen: Streamline the semantics of NAME
Summary:
The new rules are straightforward. The main rules to keep in mind
are:
1. NAME is an implicit template argument of class and multiclass,
and will be substituted by the name of the instantiating def/defm.
2. The name of a def/defm in a multiclass must contain a reference
to NAME. If such a reference is not present, it is automatically
prepended.
And for some additional subtleties, consider these:
3. defm with no name generates a unique name but has no special
behavior otherwise.
4. def with no name generates an anonymous record, whose name is
unique but undefined. In particular, the name won't contain a
reference to NAME.
Keeping rules 1&2 in mind should allow a predictable behavior of
name resolution that is simple to follow.
The old "rules" were rather surprising: sometimes (but not always),
NAME would correspond to the name of the toplevel defm. They were
also plain bonkers when you pushed them to their limits, as the old
version of the TableGen test case shows.
Having NAME correspond to the name of the toplevel defm introduces
"spooky action at a distance" and breaks composability:
refactoring the upper layers of a hierarchy of nested multiclass
instantiations can cause unexpected breakage by changing the value
of NAME at a lower level of the hierarchy. The new rules don't
suffer from this problem.
Some existing .td files have to be adjusted because they ended up
depending on the details of the old implementation.
Change-Id: I694095231565b30f563e6fd0417b41ee01a12589
Reviewers: tra, simon_tatham, craig.topper, MartinO, arsenm, javed.absar
Subscribers: wdng, llvm-commits
Differential Revision: https://reviews.llvm.org/D47430
llvm-svn: 333900
2018-06-04 22:26:05 +08:00
|
|
|
|
|
|
|
Init *HasReferenceResolver::resolve(Init *VarName)
|
|
|
|
{
|
|
|
|
if (VarName == VarNameToTrack)
|
|
|
|
Found = true;
|
|
|
|
return nullptr;
|
|
|
|
}
|