The test included with this commit shows a case where, even though a record's true type was !isa<> some unrelated class, the isa<> operator wolud use the declared type of the argument it was examining in order to conclude that the !isa<> expression had to be be false. The issues is fixed by checking to make sure that the argument to the !isa operator is fully concrete before declaring its result to be false.
3427 lines
106 KiB
C++
3427 lines
106 KiB
C++
//===- Record.cpp - Record implementation ---------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Implement the tablegen record classes.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/TableGen/Record.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/FoldingSet.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Config/llvm-config.h"
|
|
#include "llvm/Support/Allocator.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/MathExtras.h"
|
|
#include "llvm/Support/Regex.h"
|
|
#include "llvm/Support/SMLoc.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/TableGen/Error.h"
|
|
#include "llvm/TableGen/TGTimer.h"
|
|
#include <cassert>
|
|
#include <cstdint>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "tblgen-records"
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Context
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace llvm {
|
|
namespace detail {
|
|
/// This class represents the internal implementation of the RecordKeeper.
|
|
/// It contains all of the contextual static state of the Record classes. It is
|
|
/// kept out-of-line to simplify dependencies, and also make it easier for
|
|
/// internal classes to access the uniquer state of the keeper.
|
|
struct RecordKeeperImpl {
|
|
RecordKeeperImpl(RecordKeeper &RK)
|
|
: SharedBitRecTy(RK), SharedIntRecTy(RK), SharedStringRecTy(RK),
|
|
SharedDagRecTy(RK), AnyRecord(RK, 0), TheUnsetInit(RK),
|
|
TrueBitInit(true, &SharedBitRecTy),
|
|
FalseBitInit(false, &SharedBitRecTy), StringInitStringPool(Allocator),
|
|
StringInitCodePool(Allocator), AnonCounter(0), LastRecordID(0) {}
|
|
|
|
BumpPtrAllocator Allocator;
|
|
std::vector<BitsRecTy *> SharedBitsRecTys;
|
|
BitRecTy SharedBitRecTy;
|
|
IntRecTy SharedIntRecTy;
|
|
StringRecTy SharedStringRecTy;
|
|
DagRecTy SharedDagRecTy;
|
|
|
|
RecordRecTy AnyRecord;
|
|
UnsetInit TheUnsetInit;
|
|
BitInit TrueBitInit;
|
|
BitInit FalseBitInit;
|
|
|
|
FoldingSet<ArgumentInit> TheArgumentInitPool;
|
|
FoldingSet<BitsInit> TheBitsInitPool;
|
|
std::map<int64_t, IntInit *> TheIntInitPool;
|
|
StringMap<const StringInit *, BumpPtrAllocator &> StringInitStringPool;
|
|
StringMap<const StringInit *, BumpPtrAllocator &> StringInitCodePool;
|
|
FoldingSet<ListInit> TheListInitPool;
|
|
FoldingSet<UnOpInit> TheUnOpInitPool;
|
|
FoldingSet<BinOpInit> TheBinOpInitPool;
|
|
FoldingSet<TernOpInit> TheTernOpInitPool;
|
|
FoldingSet<FoldOpInit> TheFoldOpInitPool;
|
|
FoldingSet<IsAOpInit> TheIsAOpInitPool;
|
|
FoldingSet<ExistsOpInit> TheExistsOpInitPool;
|
|
DenseMap<std::pair<const RecTy *, const Init *>, VarInit *> TheVarInitPool;
|
|
DenseMap<std::pair<const TypedInit *, unsigned>, VarBitInit *>
|
|
TheVarBitInitPool;
|
|
FoldingSet<VarDefInit> TheVarDefInitPool;
|
|
DenseMap<std::pair<const Init *, const StringInit *>, FieldInit *>
|
|
TheFieldInitPool;
|
|
FoldingSet<CondOpInit> TheCondOpInitPool;
|
|
FoldingSet<DagInit> TheDagInitPool;
|
|
FoldingSet<RecordRecTy> RecordTypePool;
|
|
|
|
unsigned AnonCounter;
|
|
unsigned LastRecordID;
|
|
|
|
void dumpAllocationStats(raw_ostream &OS) const;
|
|
};
|
|
} // namespace detail
|
|
} // namespace llvm
|
|
|
|
void detail::RecordKeeperImpl::dumpAllocationStats(raw_ostream &OS) const {
|
|
// Dump memory allocation related stats.
|
|
OS << "TheArgumentInitPool size = " << TheArgumentInitPool.size() << '\n';
|
|
OS << "TheBitsInitPool size = " << TheBitsInitPool.size() << '\n';
|
|
OS << "TheIntInitPool size = " << TheIntInitPool.size() << '\n';
|
|
OS << "StringInitStringPool size = " << StringInitStringPool.size() << '\n';
|
|
OS << "StringInitCodePool size = " << StringInitCodePool.size() << '\n';
|
|
OS << "TheListInitPool size = " << TheListInitPool.size() << '\n';
|
|
OS << "TheUnOpInitPool size = " << TheUnOpInitPool.size() << '\n';
|
|
OS << "TheBinOpInitPool size = " << TheBinOpInitPool.size() << '\n';
|
|
OS << "TheTernOpInitPool size = " << TheTernOpInitPool.size() << '\n';
|
|
OS << "TheFoldOpInitPool size = " << TheFoldOpInitPool.size() << '\n';
|
|
OS << "TheIsAOpInitPool size = " << TheIsAOpInitPool.size() << '\n';
|
|
OS << "TheExistsOpInitPool size = " << TheExistsOpInitPool.size() << '\n';
|
|
OS << "TheCondOpInitPool size = " << TheCondOpInitPool.size() << '\n';
|
|
OS << "TheDagInitPool size = " << TheDagInitPool.size() << '\n';
|
|
OS << "RecordTypePool size = " << RecordTypePool.size() << '\n';
|
|
OS << "TheVarInitPool size = " << TheVarInitPool.size() << '\n';
|
|
OS << "TheVarBitInitPool size = " << TheVarBitInitPool.size() << '\n';
|
|
OS << "TheVarDefInitPool size = " << TheVarDefInitPool.size() << '\n';
|
|
OS << "TheFieldInitPool size = " << TheFieldInitPool.size() << '\n';
|
|
OS << "Bytes allocated = " << Allocator.getBytesAllocated() << '\n';
|
|
OS << "Total allocator memory = " << Allocator.getTotalMemory() << "\n\n";
|
|
|
|
OS << "Number of records instantiated = " << LastRecordID << '\n';
|
|
OS << "Number of anonymous records = " << AnonCounter << '\n';
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Type implementations
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
LLVM_DUMP_METHOD void RecTy::dump() const { print(errs()); }
|
|
#endif
|
|
|
|
const ListRecTy *RecTy::getListTy() const {
|
|
if (!ListTy)
|
|
ListTy = new (RK.getImpl().Allocator) ListRecTy(this);
|
|
return ListTy;
|
|
}
|
|
|
|
bool RecTy::typeIsConvertibleTo(const RecTy *RHS) const {
|
|
assert(RHS && "NULL pointer");
|
|
return Kind == RHS->getRecTyKind();
|
|
}
|
|
|
|
bool RecTy::typeIsA(const RecTy *RHS) const { return this == RHS; }
|
|
|
|
const BitRecTy *BitRecTy::get(RecordKeeper &RK) {
|
|
return &RK.getImpl().SharedBitRecTy;
|
|
}
|
|
|
|
bool BitRecTy::typeIsConvertibleTo(const RecTy *RHS) const{
|
|
if (RecTy::typeIsConvertibleTo(RHS) || RHS->getRecTyKind() == IntRecTyKind)
|
|
return true;
|
|
if (const auto *BitsTy = dyn_cast<BitsRecTy>(RHS))
|
|
return BitsTy->getNumBits() == 1;
|
|
return false;
|
|
}
|
|
|
|
const BitsRecTy *BitsRecTy::get(RecordKeeper &RK, unsigned Sz) {
|
|
detail::RecordKeeperImpl &RKImpl = RK.getImpl();
|
|
if (Sz >= RKImpl.SharedBitsRecTys.size())
|
|
RKImpl.SharedBitsRecTys.resize(Sz + 1);
|
|
BitsRecTy *&Ty = RKImpl.SharedBitsRecTys[Sz];
|
|
if (!Ty)
|
|
Ty = new (RKImpl.Allocator) BitsRecTy(RK, Sz);
|
|
return Ty;
|
|
}
|
|
|
|
std::string BitsRecTy::getAsString() const {
|
|
return "bits<" + utostr(Size) + ">";
|
|
}
|
|
|
|
bool BitsRecTy::typeIsConvertibleTo(const RecTy *RHS) const {
|
|
if (RecTy::typeIsConvertibleTo(RHS)) //argument and the sender are same type
|
|
return cast<BitsRecTy>(RHS)->Size == Size;
|
|
RecTyKind kind = RHS->getRecTyKind();
|
|
return (kind == BitRecTyKind && Size == 1) || (kind == IntRecTyKind);
|
|
}
|
|
|
|
const IntRecTy *IntRecTy::get(RecordKeeper &RK) {
|
|
return &RK.getImpl().SharedIntRecTy;
|
|
}
|
|
|
|
bool IntRecTy::typeIsConvertibleTo(const RecTy *RHS) const {
|
|
RecTyKind kind = RHS->getRecTyKind();
|
|
return kind==BitRecTyKind || kind==BitsRecTyKind || kind==IntRecTyKind;
|
|
}
|
|
|
|
const StringRecTy *StringRecTy::get(RecordKeeper &RK) {
|
|
return &RK.getImpl().SharedStringRecTy;
|
|
}
|
|
|
|
std::string StringRecTy::getAsString() const {
|
|
return "string";
|
|
}
|
|
|
|
bool StringRecTy::typeIsConvertibleTo(const RecTy *RHS) const {
|
|
RecTyKind Kind = RHS->getRecTyKind();
|
|
return Kind == StringRecTyKind;
|
|
}
|
|
|
|
std::string ListRecTy::getAsString() const {
|
|
return "list<" + ElementTy->getAsString() + ">";
|
|
}
|
|
|
|
bool ListRecTy::typeIsConvertibleTo(const RecTy *RHS) const {
|
|
if (const auto *ListTy = dyn_cast<ListRecTy>(RHS))
|
|
return ElementTy->typeIsConvertibleTo(ListTy->getElementType());
|
|
return false;
|
|
}
|
|
|
|
bool ListRecTy::typeIsA(const RecTy *RHS) const {
|
|
if (const auto *RHSl = dyn_cast<ListRecTy>(RHS))
|
|
return getElementType()->typeIsA(RHSl->getElementType());
|
|
return false;
|
|
}
|
|
|
|
const DagRecTy *DagRecTy::get(RecordKeeper &RK) {
|
|
return &RK.getImpl().SharedDagRecTy;
|
|
}
|
|
|
|
std::string DagRecTy::getAsString() const {
|
|
return "dag";
|
|
}
|
|
|
|
static void ProfileRecordRecTy(FoldingSetNodeID &ID,
|
|
ArrayRef<const Record *> Classes) {
|
|
ID.AddInteger(Classes.size());
|
|
for (const Record *R : Classes)
|
|
ID.AddPointer(R);
|
|
}
|
|
|
|
const RecordRecTy *RecordRecTy::get(RecordKeeper &RK,
|
|
ArrayRef<const Record *> UnsortedClasses) {
|
|
detail::RecordKeeperImpl &RKImpl = RK.getImpl();
|
|
if (UnsortedClasses.empty())
|
|
return &RKImpl.AnyRecord;
|
|
|
|
FoldingSet<RecordRecTy> &ThePool = RKImpl.RecordTypePool;
|
|
|
|
SmallVector<const Record *, 4> Classes(UnsortedClasses);
|
|
llvm::sort(Classes, [](const Record *LHS, const Record *RHS) {
|
|
return LHS->getNameInitAsString() < RHS->getNameInitAsString();
|
|
});
|
|
|
|
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 = RKImpl.Allocator.Allocate(
|
|
totalSizeToAlloc<const Record *>(Classes.size()), alignof(RecordRecTy));
|
|
RecordRecTy *Ty = new (Mem) RecordRecTy(RK, Classes.size());
|
|
std::uninitialized_copy(Classes.begin(), Classes.end(),
|
|
Ty->getTrailingObjects<const Record *>());
|
|
ThePool.InsertNode(Ty, IP);
|
|
return Ty;
|
|
}
|
|
|
|
const RecordRecTy *RecordRecTy::get(const Record *Class) {
|
|
assert(Class && "unexpected null class");
|
|
return get(Class->getRecords(), {Class});
|
|
}
|
|
|
|
void RecordRecTy::Profile(FoldingSetNodeID &ID) const {
|
|
ProfileRecordRecTy(ID, getClasses());
|
|
}
|
|
|
|
std::string RecordRecTy::getAsString() const {
|
|
if (NumClasses == 1)
|
|
return getClasses()[0]->getNameInitAsString();
|
|
|
|
std::string Str = "{";
|
|
bool First = true;
|
|
for (const Record *R : getClasses()) {
|
|
if (!First)
|
|
Str += ", ";
|
|
First = false;
|
|
Str += R->getNameInitAsString();
|
|
}
|
|
Str += "}";
|
|
return Str;
|
|
}
|
|
|
|
bool RecordRecTy::isSubClassOf(const Record *Class) const {
|
|
return llvm::any_of(getClasses(), [Class](const Record *MySuperClass) {
|
|
return MySuperClass == Class || MySuperClass->isSubClassOf(Class);
|
|
});
|
|
}
|
|
|
|
bool RecordRecTy::typeIsConvertibleTo(const RecTy *RHS) const {
|
|
if (this == RHS)
|
|
return true;
|
|
|
|
const auto *RTy = dyn_cast<RecordRecTy>(RHS);
|
|
if (!RTy)
|
|
return false;
|
|
|
|
return llvm::all_of(RTy->getClasses(), [this](const Record *TargetClass) {
|
|
return isSubClassOf(TargetClass);
|
|
});
|
|
}
|
|
|
|
bool RecordRecTy::typeIsA(const RecTy *RHS) const {
|
|
return typeIsConvertibleTo(RHS);
|
|
}
|
|
|
|
static const RecordRecTy *resolveRecordTypes(const RecordRecTy *T1,
|
|
const RecordRecTy *T2) {
|
|
SmallVector<const Record *, 4> CommonSuperClasses;
|
|
SmallVector<const Record *, 4> Stack(T1->getClasses());
|
|
|
|
while (!Stack.empty()) {
|
|
const Record *R = Stack.pop_back_val();
|
|
|
|
if (T2->isSubClassOf(R)) {
|
|
CommonSuperClasses.push_back(R);
|
|
} else {
|
|
R->getDirectSuperClasses(Stack);
|
|
}
|
|
}
|
|
|
|
return RecordRecTy::get(T1->getRecordKeeper(), CommonSuperClasses);
|
|
}
|
|
|
|
const RecTy *llvm::resolveTypes(const RecTy *T1, const RecTy *T2) {
|
|
if (T1 == T2)
|
|
return T1;
|
|
|
|
if (const auto *RecTy1 = dyn_cast<RecordRecTy>(T1)) {
|
|
if (const auto *RecTy2 = dyn_cast<RecordRecTy>(T2))
|
|
return resolveRecordTypes(RecTy1, RecTy2);
|
|
}
|
|
|
|
assert(T1 != nullptr && "Invalid record type");
|
|
if (T1->typeIsConvertibleTo(T2))
|
|
return T2;
|
|
|
|
assert(T2 != nullptr && "Invalid record type");
|
|
if (T2->typeIsConvertibleTo(T1))
|
|
return T1;
|
|
|
|
if (const auto *ListTy1 = dyn_cast<ListRecTy>(T1)) {
|
|
if (const auto *ListTy2 = dyn_cast<ListRecTy>(T2)) {
|
|
const RecTy *NewType =
|
|
resolveTypes(ListTy1->getElementType(), ListTy2->getElementType());
|
|
if (NewType)
|
|
return NewType->getListTy();
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Initializer implementations
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void Init::anchor() {}
|
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
LLVM_DUMP_METHOD void Init::dump() const { return print(errs()); }
|
|
#endif
|
|
|
|
RecordKeeper &Init::getRecordKeeper() const {
|
|
if (auto *TyInit = dyn_cast<TypedInit>(this))
|
|
return TyInit->getType()->getRecordKeeper();
|
|
if (auto *ArgInit = dyn_cast<ArgumentInit>(this))
|
|
return ArgInit->getRecordKeeper();
|
|
return cast<UnsetInit>(this)->getRecordKeeper();
|
|
}
|
|
|
|
UnsetInit *UnsetInit::get(RecordKeeper &RK) {
|
|
return &RK.getImpl().TheUnsetInit;
|
|
}
|
|
|
|
const Init *UnsetInit::getCastTo(const RecTy *Ty) const { return this; }
|
|
|
|
const Init *UnsetInit::convertInitializerTo(const RecTy *Ty) const {
|
|
return this;
|
|
}
|
|
|
|
static void ProfileArgumentInit(FoldingSetNodeID &ID, const Init *Value,
|
|
ArgAuxType Aux) {
|
|
auto I = Aux.index();
|
|
ID.AddInteger(I);
|
|
if (I == ArgumentInit::Positional)
|
|
ID.AddInteger(std::get<ArgumentInit::Positional>(Aux));
|
|
if (I == ArgumentInit::Named)
|
|
ID.AddPointer(std::get<ArgumentInit::Named>(Aux));
|
|
ID.AddPointer(Value);
|
|
}
|
|
|
|
void ArgumentInit::Profile(FoldingSetNodeID &ID) const {
|
|
ProfileArgumentInit(ID, Value, Aux);
|
|
}
|
|
|
|
const ArgumentInit *ArgumentInit::get(const Init *Value, ArgAuxType Aux) {
|
|
FoldingSetNodeID ID;
|
|
ProfileArgumentInit(ID, Value, Aux);
|
|
|
|
RecordKeeper &RK = Value->getRecordKeeper();
|
|
detail::RecordKeeperImpl &RKImpl = RK.getImpl();
|
|
void *IP = nullptr;
|
|
if (const ArgumentInit *I =
|
|
RKImpl.TheArgumentInitPool.FindNodeOrInsertPos(ID, IP))
|
|
return I;
|
|
|
|
ArgumentInit *I = new (RKImpl.Allocator) ArgumentInit(Value, Aux);
|
|
RKImpl.TheArgumentInitPool.InsertNode(I, IP);
|
|
return I;
|
|
}
|
|
|
|
const Init *ArgumentInit::resolveReferences(Resolver &R) const {
|
|
const Init *NewValue = Value->resolveReferences(R);
|
|
if (NewValue != Value)
|
|
return cloneWithValue(NewValue);
|
|
|
|
return this;
|
|
}
|
|
|
|
BitInit *BitInit::get(RecordKeeper &RK, bool V) {
|
|
return V ? &RK.getImpl().TrueBitInit : &RK.getImpl().FalseBitInit;
|
|
}
|
|
|
|
const Init *BitInit::convertInitializerTo(const RecTy *Ty) const {
|
|
if (isa<BitRecTy>(Ty))
|
|
return this;
|
|
|
|
if (isa<IntRecTy>(Ty))
|
|
return IntInit::get(getRecordKeeper(), getValue());
|
|
|
|
if (auto *BRT = dyn_cast<BitsRecTy>(Ty)) {
|
|
// Can only convert single bit.
|
|
if (BRT->getNumBits() == 1)
|
|
return BitsInit::get(getRecordKeeper(), this);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static void ProfileBitsInit(FoldingSetNodeID &ID,
|
|
ArrayRef<const Init *> Range) {
|
|
ID.AddInteger(Range.size());
|
|
|
|
for (const Init *I : Range)
|
|
ID.AddPointer(I);
|
|
}
|
|
|
|
BitsInit *BitsInit::get(RecordKeeper &RK, ArrayRef<const Init *> Range) {
|
|
FoldingSetNodeID ID;
|
|
ProfileBitsInit(ID, Range);
|
|
|
|
detail::RecordKeeperImpl &RKImpl = RK.getImpl();
|
|
void *IP = nullptr;
|
|
if (BitsInit *I = RKImpl.TheBitsInitPool.FindNodeOrInsertPos(ID, IP))
|
|
return I;
|
|
|
|
void *Mem = RKImpl.Allocator.Allocate(
|
|
totalSizeToAlloc<const Init *>(Range.size()), alignof(BitsInit));
|
|
BitsInit *I = new (Mem) BitsInit(RK, Range.size());
|
|
std::uninitialized_copy(Range.begin(), Range.end(),
|
|
I->getTrailingObjects<const Init *>());
|
|
RKImpl.TheBitsInitPool.InsertNode(I, IP);
|
|
return I;
|
|
}
|
|
|
|
void BitsInit::Profile(FoldingSetNodeID &ID) const {
|
|
ProfileBitsInit(ID, ArrayRef(getTrailingObjects<const Init *>(), NumBits));
|
|
}
|
|
|
|
const Init *BitsInit::convertInitializerTo(const 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 this;
|
|
}
|
|
|
|
if (isa<IntRecTy>(Ty)) {
|
|
std::optional<int64_t> Result = convertInitializerToInt();
|
|
if (Result)
|
|
return IntInit::get(getRecordKeeper(), *Result);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
std::optional<int64_t> BitsInit::convertInitializerToInt() const {
|
|
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 std::nullopt;
|
|
return Result;
|
|
}
|
|
|
|
const Init *
|
|
BitsInit::convertInitializerBitRange(ArrayRef<unsigned> Bits) const {
|
|
SmallVector<const Init *, 16> NewBits(Bits.size());
|
|
|
|
for (unsigned i = 0, e = Bits.size(); i != e; ++i) {
|
|
if (Bits[i] >= getNumBits())
|
|
return nullptr;
|
|
NewBits[i] = getBit(Bits[i]);
|
|
}
|
|
return BitsInit::get(getRecordKeeper(), NewBits);
|
|
}
|
|
|
|
bool BitsInit::isConcrete() const {
|
|
for (unsigned i = 0, e = getNumBits(); i != e; ++i) {
|
|
if (!getBit(i)->isConcrete())
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string BitsInit::getAsString() const {
|
|
std::string Result = "{ ";
|
|
for (unsigned i = 0, e = getNumBits(); i != e; ++i) {
|
|
if (i) Result += ", ";
|
|
if (const Init *Bit = getBit(e - i - 1))
|
|
Result += Bit->getAsString();
|
|
else
|
|
Result += "*";
|
|
}
|
|
return Result + " }";
|
|
}
|
|
|
|
// resolveReferences - If there are any field references that refer to fields
|
|
// that have been filled in, we can propagate the values now.
|
|
const Init *BitsInit::resolveReferences(Resolver &R) const {
|
|
bool Changed = false;
|
|
SmallVector<const Init *, 16> NewBits(getNumBits());
|
|
|
|
const Init *CachedBitVarRef = nullptr;
|
|
const Init *CachedBitVarResolved = nullptr;
|
|
|
|
for (unsigned i = 0, e = getNumBits(); i != e; ++i) {
|
|
const Init *CurBit = getBit(i);
|
|
const Init *NewBit = CurBit;
|
|
|
|
if (const auto *CurBitVar = dyn_cast<VarBitInit>(CurBit)) {
|
|
if (CurBitVar->getBitVar() != CachedBitVarRef) {
|
|
CachedBitVarRef = CurBitVar->getBitVar();
|
|
CachedBitVarResolved = CachedBitVarRef->resolveReferences(R);
|
|
}
|
|
assert(CachedBitVarResolved && "Unresolved bitvar reference");
|
|
NewBit = CachedBitVarResolved->getBit(CurBitVar->getBitNum());
|
|
} else {
|
|
// getBit(0) implicitly converts int and bits<1> values to bit.
|
|
NewBit = CurBit->resolveReferences(R)->getBit(0);
|
|
}
|
|
|
|
if (isa<UnsetInit>(NewBit) && R.keepUnsetBits())
|
|
NewBit = CurBit;
|
|
NewBits[i] = NewBit;
|
|
Changed |= CurBit != NewBit;
|
|
}
|
|
|
|
if (Changed)
|
|
return BitsInit::get(getRecordKeeper(), NewBits);
|
|
|
|
return this;
|
|
}
|
|
|
|
IntInit *IntInit::get(RecordKeeper &RK, int64_t V) {
|
|
IntInit *&I = RK.getImpl().TheIntInitPool[V];
|
|
if (!I)
|
|
I = new (RK.getImpl().Allocator) IntInit(RK, V);
|
|
return I;
|
|
}
|
|
|
|
std::string IntInit::getAsString() const {
|
|
return itostr(Value);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
const Init *IntInit::convertInitializerTo(const RecTy *Ty) const {
|
|
if (isa<IntRecTy>(Ty))
|
|
return 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(getRecordKeeper(), Val != 0);
|
|
}
|
|
|
|
if (const 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<const Init *, 16> NewBits(BRT->getNumBits());
|
|
for (unsigned i = 0; i != BRT->getNumBits(); ++i)
|
|
NewBits[i] =
|
|
BitInit::get(getRecordKeeper(), Value & ((i < 64) ? (1LL << i) : 0));
|
|
|
|
return BitsInit::get(getRecordKeeper(), NewBits);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
const Init *IntInit::convertInitializerBitRange(ArrayRef<unsigned> Bits) const {
|
|
SmallVector<const Init *, 16> NewBits(Bits.size());
|
|
|
|
for (unsigned i = 0, e = Bits.size(); i != e; ++i) {
|
|
if (Bits[i] >= 64)
|
|
return nullptr;
|
|
|
|
NewBits[i] =
|
|
BitInit::get(getRecordKeeper(), Value & (INT64_C(1) << Bits[i]));
|
|
}
|
|
return BitsInit::get(getRecordKeeper(), NewBits);
|
|
}
|
|
|
|
AnonymousNameInit *AnonymousNameInit::get(RecordKeeper &RK, unsigned V) {
|
|
return new (RK.getImpl().Allocator) AnonymousNameInit(RK, V);
|
|
}
|
|
|
|
const StringInit *AnonymousNameInit::getNameInit() const {
|
|
return StringInit::get(getRecordKeeper(), getAsString());
|
|
}
|
|
|
|
std::string AnonymousNameInit::getAsString() const {
|
|
return "anonymous_" + utostr(Value);
|
|
}
|
|
|
|
const Init *AnonymousNameInit::resolveReferences(Resolver &R) const {
|
|
auto *Old = this;
|
|
auto *New = R.resolve(Old);
|
|
New = New ? New : Old;
|
|
if (R.isFinal())
|
|
if (const auto *Anonymous = dyn_cast<AnonymousNameInit>(New))
|
|
return Anonymous->getNameInit();
|
|
return New;
|
|
}
|
|
|
|
const StringInit *StringInit::get(RecordKeeper &RK, StringRef V,
|
|
StringFormat Fmt) {
|
|
detail::RecordKeeperImpl &RKImpl = RK.getImpl();
|
|
auto &InitMap = Fmt == SF_String ? RKImpl.StringInitStringPool
|
|
: RKImpl.StringInitCodePool;
|
|
auto &Entry = *InitMap.try_emplace(V, nullptr).first;
|
|
if (!Entry.second)
|
|
Entry.second = new (RKImpl.Allocator) StringInit(RK, Entry.getKey(), Fmt);
|
|
return Entry.second;
|
|
}
|
|
|
|
const Init *StringInit::convertInitializerTo(const RecTy *Ty) const {
|
|
if (isa<StringRecTy>(Ty))
|
|
return this;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static void ProfileListInit(FoldingSetNodeID &ID, ArrayRef<const Init *> Range,
|
|
const RecTy *EltTy) {
|
|
ID.AddInteger(Range.size());
|
|
ID.AddPointer(EltTy);
|
|
|
|
for (const Init *I : Range)
|
|
ID.AddPointer(I);
|
|
}
|
|
|
|
const ListInit *ListInit::get(ArrayRef<const Init *> Range,
|
|
const RecTy *EltTy) {
|
|
FoldingSetNodeID ID;
|
|
ProfileListInit(ID, Range, EltTy);
|
|
|
|
detail::RecordKeeperImpl &RK = EltTy->getRecordKeeper().getImpl();
|
|
void *IP = nullptr;
|
|
if (const ListInit *I = RK.TheListInitPool.FindNodeOrInsertPos(ID, IP))
|
|
return I;
|
|
|
|
assert(Range.empty() || !isa<TypedInit>(Range[0]) ||
|
|
cast<TypedInit>(Range[0])->getType()->typeIsConvertibleTo(EltTy));
|
|
|
|
void *Mem = RK.Allocator.Allocate(
|
|
totalSizeToAlloc<const Init *>(Range.size()), alignof(ListInit));
|
|
ListInit *I = new (Mem) ListInit(Range.size(), EltTy);
|
|
std::uninitialized_copy(Range.begin(), Range.end(),
|
|
I->getTrailingObjects<const Init *>());
|
|
RK.TheListInitPool.InsertNode(I, IP);
|
|
return I;
|
|
}
|
|
|
|
void ListInit::Profile(FoldingSetNodeID &ID) const {
|
|
const RecTy *EltTy = cast<ListRecTy>(getType())->getElementType();
|
|
|
|
ProfileListInit(ID, getValues(), EltTy);
|
|
}
|
|
|
|
const Init *ListInit::convertInitializerTo(const RecTy *Ty) const {
|
|
if (getType() == Ty)
|
|
return this;
|
|
|
|
if (const auto *LRT = dyn_cast<ListRecTy>(Ty)) {
|
|
SmallVector<const Init *, 8> Elements;
|
|
Elements.reserve(getValues().size());
|
|
|
|
// Verify that all of the elements of the list are subclasses of the
|
|
// appropriate class!
|
|
bool Changed = false;
|
|
const RecTy *ElementType = LRT->getElementType();
|
|
for (const Init *I : getValues())
|
|
if (const Init *CI = I->convertInitializerTo(ElementType)) {
|
|
Elements.push_back(CI);
|
|
if (CI != I)
|
|
Changed = true;
|
|
} else
|
|
return nullptr;
|
|
|
|
if (!Changed)
|
|
return this;
|
|
return ListInit::get(Elements, ElementType);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
const Record *ListInit::getElementAsRecord(unsigned i) const {
|
|
assert(i < NumValues && "List element index out of range!");
|
|
const auto *DI = dyn_cast<DefInit>(getElement(i));
|
|
if (!DI)
|
|
PrintFatalError("Expected record in list!");
|
|
return DI->getDef();
|
|
}
|
|
|
|
const Init *ListInit::resolveReferences(Resolver &R) const {
|
|
SmallVector<const Init *, 8> Resolved;
|
|
Resolved.reserve(size());
|
|
bool Changed = false;
|
|
|
|
for (const Init *CurElt : getValues()) {
|
|
const Init *E = CurElt->resolveReferences(R);
|
|
Changed |= E != CurElt;
|
|
Resolved.push_back(E);
|
|
}
|
|
|
|
if (Changed)
|
|
return ListInit::get(Resolved, getElementType());
|
|
return this;
|
|
}
|
|
|
|
bool ListInit::isComplete() const {
|
|
for (const Init *Element : *this) {
|
|
if (!Element->isComplete())
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ListInit::isConcrete() const {
|
|
for (const Init *Element : *this) {
|
|
if (!Element->isConcrete())
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string ListInit::getAsString() const {
|
|
std::string Result = "[";
|
|
const char *sep = "";
|
|
for (const Init *Element : *this) {
|
|
Result += sep;
|
|
sep = ", ";
|
|
Result += Element->getAsString();
|
|
}
|
|
return Result + "]";
|
|
}
|
|
|
|
const Init *OpInit::getBit(unsigned Bit) const {
|
|
if (getType() == BitRecTy::get(getRecordKeeper()))
|
|
return this;
|
|
return VarBitInit::get(this, Bit);
|
|
}
|
|
|
|
static void ProfileUnOpInit(FoldingSetNodeID &ID, unsigned Opcode,
|
|
const Init *Op, const RecTy *Type) {
|
|
ID.AddInteger(Opcode);
|
|
ID.AddPointer(Op);
|
|
ID.AddPointer(Type);
|
|
}
|
|
|
|
const UnOpInit *UnOpInit::get(UnaryOp Opc, const Init *LHS, const RecTy *Type) {
|
|
FoldingSetNodeID ID;
|
|
ProfileUnOpInit(ID, Opc, LHS, Type);
|
|
|
|
detail::RecordKeeperImpl &RK = Type->getRecordKeeper().getImpl();
|
|
void *IP = nullptr;
|
|
if (const UnOpInit *I = RK.TheUnOpInitPool.FindNodeOrInsertPos(ID, IP))
|
|
return I;
|
|
|
|
UnOpInit *I = new (RK.Allocator) UnOpInit(Opc, LHS, Type);
|
|
RK.TheUnOpInitPool.InsertNode(I, IP);
|
|
return I;
|
|
}
|
|
|
|
void UnOpInit::Profile(FoldingSetNodeID &ID) const {
|
|
ProfileUnOpInit(ID, getOpcode(), getOperand(), getType());
|
|
}
|
|
|
|
const Init *UnOpInit::Fold(const Record *CurRec, bool IsFinal) const {
|
|
RecordKeeper &RK = getRecordKeeper();
|
|
switch (getOpcode()) {
|
|
case REPR:
|
|
if (LHS->isConcrete()) {
|
|
// If it is a Record, print the full content.
|
|
if (const auto *Def = dyn_cast<DefInit>(LHS)) {
|
|
std::string S;
|
|
raw_string_ostream OS(S);
|
|
OS << *Def->getDef();
|
|
return StringInit::get(RK, S);
|
|
} else {
|
|
// Otherwise, print the value of the variable.
|
|
//
|
|
// NOTE: we could recursively !repr the elements of a list,
|
|
// but that could produce a lot of output when printing a
|
|
// defset.
|
|
return StringInit::get(RK, LHS->getAsString());
|
|
}
|
|
}
|
|
break;
|
|
case TOLOWER:
|
|
if (const auto *LHSs = dyn_cast<StringInit>(LHS))
|
|
return StringInit::get(RK, LHSs->getValue().lower());
|
|
break;
|
|
case TOUPPER:
|
|
if (const auto *LHSs = dyn_cast<StringInit>(LHS))
|
|
return StringInit::get(RK, LHSs->getValue().upper());
|
|
break;
|
|
case CAST:
|
|
if (isa<StringRecTy>(getType())) {
|
|
if (const auto *LHSs = dyn_cast<StringInit>(LHS))
|
|
return LHSs;
|
|
|
|
if (const auto *LHSd = dyn_cast<DefInit>(LHS))
|
|
return StringInit::get(RK, LHSd->getAsString());
|
|
|
|
if (const auto *LHSi = dyn_cast_or_null<IntInit>(
|
|
LHS->convertInitializerTo(IntRecTy::get(RK))))
|
|
return StringInit::get(RK, LHSi->getAsString());
|
|
|
|
} else if (isa<RecordRecTy>(getType())) {
|
|
if (const auto *Name = dyn_cast<StringInit>(LHS)) {
|
|
const Record *D = RK.getDef(Name->getValue());
|
|
if (!D && CurRec) {
|
|
// Self-references are allowed, but their resolution is delayed until
|
|
// the final resolve to ensure that we get the correct type for them.
|
|
auto *Anonymous = dyn_cast<AnonymousNameInit>(CurRec->getNameInit());
|
|
if (Name == CurRec->getNameInit() ||
|
|
(Anonymous && Name == Anonymous->getNameInit())) {
|
|
if (!IsFinal)
|
|
break;
|
|
D = CurRec;
|
|
}
|
|
}
|
|
|
|
auto PrintFatalErrorHelper = [CurRec](const Twine &T) {
|
|
if (CurRec)
|
|
PrintFatalError(CurRec->getLoc(), T);
|
|
else
|
|
PrintFatalError(T);
|
|
};
|
|
|
|
if (!D) {
|
|
if (IsFinal) {
|
|
PrintFatalErrorHelper(Twine("Undefined reference to record: '") +
|
|
Name->getValue() + "'\n");
|
|
}
|
|
break;
|
|
}
|
|
|
|
DefInit *DI = D->getDefInit();
|
|
if (!DI->getType()->typeIsA(getType())) {
|
|
PrintFatalErrorHelper(Twine("Expected type '") +
|
|
getType()->getAsString() + "', got '" +
|
|
DI->getType()->getAsString() + "' in: " +
|
|
getAsString() + "\n");
|
|
}
|
|
return DI;
|
|
}
|
|
}
|
|
|
|
if (const Init *NewInit = LHS->convertInitializerTo(getType()))
|
|
return NewInit;
|
|
break;
|
|
|
|
case INITIALIZED:
|
|
if (isa<UnsetInit>(LHS))
|
|
return IntInit::get(RK, 0);
|
|
if (LHS->isConcrete())
|
|
return IntInit::get(RK, 1);
|
|
break;
|
|
|
|
case NOT:
|
|
if (const auto *LHSi = dyn_cast_or_null<IntInit>(
|
|
LHS->convertInitializerTo(IntRecTy::get(RK))))
|
|
return IntInit::get(RK, LHSi->getValue() ? 0 : 1);
|
|
break;
|
|
|
|
case HEAD:
|
|
if (const auto *LHSl = dyn_cast<ListInit>(LHS)) {
|
|
assert(!LHSl->empty() && "Empty list in head");
|
|
return LHSl->getElement(0);
|
|
}
|
|
break;
|
|
|
|
case TAIL:
|
|
if (const auto *LHSl = dyn_cast<ListInit>(LHS)) {
|
|
assert(!LHSl->empty() && "Empty list in tail");
|
|
// Note the +1. We can't just pass the result of getValues()
|
|
// directly.
|
|
return ListInit::get(LHSl->getValues().slice(1), LHSl->getElementType());
|
|
}
|
|
break;
|
|
|
|
case SIZE:
|
|
if (const auto *LHSl = dyn_cast<ListInit>(LHS))
|
|
return IntInit::get(RK, LHSl->size());
|
|
if (const auto *LHSd = dyn_cast<DagInit>(LHS))
|
|
return IntInit::get(RK, LHSd->arg_size());
|
|
if (const auto *LHSs = dyn_cast<StringInit>(LHS))
|
|
return IntInit::get(RK, LHSs->getValue().size());
|
|
break;
|
|
|
|
case EMPTY:
|
|
if (const auto *LHSl = dyn_cast<ListInit>(LHS))
|
|
return IntInit::get(RK, LHSl->empty());
|
|
if (const auto *LHSd = dyn_cast<DagInit>(LHS))
|
|
return IntInit::get(RK, LHSd->arg_empty());
|
|
if (const auto *LHSs = dyn_cast<StringInit>(LHS))
|
|
return IntInit::get(RK, LHSs->getValue().empty());
|
|
break;
|
|
|
|
case GETDAGOP:
|
|
if (const auto *Dag = dyn_cast<DagInit>(LHS)) {
|
|
// TI is not necessarily a def due to the late resolution in multiclasses,
|
|
// but has to be a TypedInit.
|
|
auto *TI = cast<TypedInit>(Dag->getOperator());
|
|
if (!TI->getType()->typeIsA(getType())) {
|
|
PrintFatalError(CurRec->getLoc(),
|
|
Twine("Expected type '") + getType()->getAsString() +
|
|
"', got '" + TI->getType()->getAsString() +
|
|
"' in: " + getAsString() + "\n");
|
|
} else {
|
|
return Dag->getOperator();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LOG2:
|
|
if (const auto *LHSi = dyn_cast_or_null<IntInit>(
|
|
LHS->convertInitializerTo(IntRecTy::get(RK)))) {
|
|
int64_t LHSv = LHSi->getValue();
|
|
if (LHSv <= 0) {
|
|
PrintFatalError(CurRec->getLoc(),
|
|
"Illegal operation: logtwo is undefined "
|
|
"on arguments less than or equal to 0");
|
|
} else {
|
|
uint64_t Log = Log2_64(LHSv);
|
|
assert(Log <= INT64_MAX &&
|
|
"Log of an int64_t must be smaller than INT64_MAX");
|
|
return IntInit::get(RK, static_cast<int64_t>(Log));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LISTFLATTEN:
|
|
if (const auto *LHSList = dyn_cast<ListInit>(LHS)) {
|
|
const auto *InnerListTy = dyn_cast<ListRecTy>(LHSList->getElementType());
|
|
// list of non-lists, !listflatten() is a NOP.
|
|
if (!InnerListTy)
|
|
return LHS;
|
|
|
|
auto Flatten =
|
|
[](const ListInit *List) -> std::optional<std::vector<const Init *>> {
|
|
std::vector<const Init *> Flattened;
|
|
// Concatenate elements of all the inner lists.
|
|
for (const Init *InnerInit : List->getValues()) {
|
|
const auto *InnerList = dyn_cast<ListInit>(InnerInit);
|
|
if (!InnerList)
|
|
return std::nullopt;
|
|
for (const Init *InnerElem : InnerList->getValues())
|
|
Flattened.push_back(InnerElem);
|
|
};
|
|
return Flattened;
|
|
};
|
|
|
|
auto Flattened = Flatten(LHSList);
|
|
if (Flattened)
|
|
return ListInit::get(*Flattened, InnerListTy->getElementType());
|
|
}
|
|
break;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
const Init *UnOpInit::resolveReferences(Resolver &R) const {
|
|
const Init *lhs = LHS->resolveReferences(R);
|
|
|
|
if (LHS != lhs || (R.isFinal() && getOpcode() == CAST))
|
|
return (UnOpInit::get(getOpcode(), lhs, getType()))
|
|
->Fold(R.getCurrentRecord(), R.isFinal());
|
|
return this;
|
|
}
|
|
|
|
std::string UnOpInit::getAsString() const {
|
|
std::string Result;
|
|
switch (getOpcode()) {
|
|
case CAST: Result = "!cast<" + getType()->getAsString() + ">"; break;
|
|
case NOT: Result = "!not"; break;
|
|
case HEAD: Result = "!head"; break;
|
|
case TAIL: Result = "!tail"; break;
|
|
case SIZE: Result = "!size"; break;
|
|
case EMPTY: Result = "!empty"; break;
|
|
case GETDAGOP: Result = "!getdagop"; break;
|
|
case LOG2 : Result = "!logtwo"; break;
|
|
case LISTFLATTEN:
|
|
Result = "!listflatten";
|
|
break;
|
|
case REPR:
|
|
Result = "!repr";
|
|
break;
|
|
case TOLOWER:
|
|
Result = "!tolower";
|
|
break;
|
|
case TOUPPER:
|
|
Result = "!toupper";
|
|
break;
|
|
case INITIALIZED:
|
|
Result = "!initialized";
|
|
break;
|
|
}
|
|
return Result + "(" + LHS->getAsString() + ")";
|
|
}
|
|
|
|
static void ProfileBinOpInit(FoldingSetNodeID &ID, unsigned Opcode,
|
|
const Init *LHS, const Init *RHS,
|
|
const RecTy *Type) {
|
|
ID.AddInteger(Opcode);
|
|
ID.AddPointer(LHS);
|
|
ID.AddPointer(RHS);
|
|
ID.AddPointer(Type);
|
|
}
|
|
|
|
const BinOpInit *BinOpInit::get(BinaryOp Opc, const Init *LHS, const Init *RHS,
|
|
const RecTy *Type) {
|
|
FoldingSetNodeID ID;
|
|
ProfileBinOpInit(ID, Opc, LHS, RHS, Type);
|
|
|
|
detail::RecordKeeperImpl &RK = LHS->getRecordKeeper().getImpl();
|
|
void *IP = nullptr;
|
|
if (const BinOpInit *I = RK.TheBinOpInitPool.FindNodeOrInsertPos(ID, IP))
|
|
return I;
|
|
|
|
BinOpInit *I = new (RK.Allocator) BinOpInit(Opc, LHS, RHS, Type);
|
|
RK.TheBinOpInitPool.InsertNode(I, IP);
|
|
return I;
|
|
}
|
|
|
|
void BinOpInit::Profile(FoldingSetNodeID &ID) const {
|
|
ProfileBinOpInit(ID, getOpcode(), getLHS(), getRHS(), getType());
|
|
}
|
|
|
|
static const StringInit *ConcatStringInits(const StringInit *I0,
|
|
const StringInit *I1) {
|
|
SmallString<80> Concat(I0->getValue());
|
|
Concat.append(I1->getValue());
|
|
return StringInit::get(
|
|
I0->getRecordKeeper(), Concat,
|
|
StringInit::determineFormat(I0->getFormat(), I1->getFormat()));
|
|
}
|
|
|
|
static const StringInit *interleaveStringList(const ListInit *List,
|
|
const StringInit *Delim) {
|
|
if (List->size() == 0)
|
|
return StringInit::get(List->getRecordKeeper(), "");
|
|
const auto *Element = dyn_cast<StringInit>(List->getElement(0));
|
|
if (!Element)
|
|
return nullptr;
|
|
SmallString<80> Result(Element->getValue());
|
|
StringInit::StringFormat Fmt = StringInit::SF_String;
|
|
|
|
for (unsigned I = 1, E = List->size(); I < E; ++I) {
|
|
Result.append(Delim->getValue());
|
|
const auto *Element = dyn_cast<StringInit>(List->getElement(I));
|
|
if (!Element)
|
|
return nullptr;
|
|
Result.append(Element->getValue());
|
|
Fmt = StringInit::determineFormat(Fmt, Element->getFormat());
|
|
}
|
|
return StringInit::get(List->getRecordKeeper(), Result, Fmt);
|
|
}
|
|
|
|
static const StringInit *interleaveIntList(const ListInit *List,
|
|
const StringInit *Delim) {
|
|
RecordKeeper &RK = List->getRecordKeeper();
|
|
if (List->size() == 0)
|
|
return StringInit::get(RK, "");
|
|
const auto *Element = dyn_cast_or_null<IntInit>(
|
|
List->getElement(0)->convertInitializerTo(IntRecTy::get(RK)));
|
|
if (!Element)
|
|
return nullptr;
|
|
SmallString<80> Result(Element->getAsString());
|
|
|
|
for (unsigned I = 1, E = List->size(); I < E; ++I) {
|
|
Result.append(Delim->getValue());
|
|
const auto *Element = dyn_cast_or_null<IntInit>(
|
|
List->getElement(I)->convertInitializerTo(IntRecTy::get(RK)));
|
|
if (!Element)
|
|
return nullptr;
|
|
Result.append(Element->getAsString());
|
|
}
|
|
return StringInit::get(RK, Result);
|
|
}
|
|
|
|
const Init *BinOpInit::getStrConcat(const Init *I0, const Init *I1) {
|
|
// Shortcut for the common case of concatenating two strings.
|
|
if (const auto *I0s = dyn_cast<StringInit>(I0))
|
|
if (const auto *I1s = dyn_cast<StringInit>(I1))
|
|
return ConcatStringInits(I0s, I1s);
|
|
return BinOpInit::get(BinOpInit::STRCONCAT, I0, I1,
|
|
StringRecTy::get(I0->getRecordKeeper()));
|
|
}
|
|
|
|
static const ListInit *ConcatListInits(const ListInit *LHS,
|
|
const ListInit *RHS) {
|
|
SmallVector<const Init *, 8> Args;
|
|
llvm::append_range(Args, *LHS);
|
|
llvm::append_range(Args, *RHS);
|
|
return ListInit::get(Args, LHS->getElementType());
|
|
}
|
|
|
|
const Init *BinOpInit::getListConcat(const TypedInit *LHS, const Init *RHS) {
|
|
assert(isa<ListRecTy>(LHS->getType()) && "First arg must be a list");
|
|
|
|
// Shortcut for the common case of concatenating two lists.
|
|
if (const auto *LHSList = dyn_cast<ListInit>(LHS))
|
|
if (const auto *RHSList = dyn_cast<ListInit>(RHS))
|
|
return ConcatListInits(LHSList, RHSList);
|
|
return BinOpInit::get(BinOpInit::LISTCONCAT, LHS, RHS, LHS->getType());
|
|
}
|
|
|
|
std::optional<bool> BinOpInit::CompareInit(unsigned Opc, const Init *LHS,
|
|
const Init *RHS) const {
|
|
// First see if we have two bit, bits, or int.
|
|
const auto *LHSi = dyn_cast_or_null<IntInit>(
|
|
LHS->convertInitializerTo(IntRecTy::get(getRecordKeeper())));
|
|
const auto *RHSi = dyn_cast_or_null<IntInit>(
|
|
RHS->convertInitializerTo(IntRecTy::get(getRecordKeeper())));
|
|
|
|
if (LHSi && RHSi) {
|
|
bool Result;
|
|
switch (Opc) {
|
|
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;
|
|
default:
|
|
llvm_unreachable("unhandled comparison");
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
// Next try strings.
|
|
const auto *LHSs = dyn_cast<StringInit>(LHS);
|
|
const auto *RHSs = dyn_cast<StringInit>(RHS);
|
|
|
|
if (LHSs && RHSs) {
|
|
bool Result;
|
|
switch (Opc) {
|
|
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");
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
// Finally, !eq and !ne can be used with records.
|
|
if (Opc == EQ || Opc == NE) {
|
|
const auto *LHSd = dyn_cast<DefInit>(LHS);
|
|
const auto *RHSd = dyn_cast<DefInit>(RHS);
|
|
if (LHSd && RHSd)
|
|
return (Opc == EQ) ? LHSd == RHSd : LHSd != RHSd;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
static std::optional<unsigned>
|
|
getDagArgNoByKey(const DagInit *Dag, const Init *Key, std::string &Error) {
|
|
// Accessor by index
|
|
if (const auto *Idx = dyn_cast<IntInit>(Key)) {
|
|
int64_t Pos = Idx->getValue();
|
|
if (Pos < 0) {
|
|
// The index is negative.
|
|
Error =
|
|
(Twine("index ") + std::to_string(Pos) + Twine(" is negative")).str();
|
|
return std::nullopt;
|
|
}
|
|
if (Pos >= Dag->getNumArgs()) {
|
|
// The index is out-of-range.
|
|
Error = (Twine("index ") + std::to_string(Pos) +
|
|
" is out of range (dag has " +
|
|
std::to_string(Dag->getNumArgs()) + " arguments)")
|
|
.str();
|
|
return std::nullopt;
|
|
}
|
|
return Pos;
|
|
}
|
|
assert(isa<StringInit>(Key));
|
|
// Accessor by name
|
|
const auto *Name = dyn_cast<StringInit>(Key);
|
|
auto ArgNo = Dag->getArgNo(Name->getValue());
|
|
if (!ArgNo) {
|
|
// The key is not found.
|
|
Error = (Twine("key '") + Name->getValue() + Twine("' is not found")).str();
|
|
return std::nullopt;
|
|
}
|
|
return *ArgNo;
|
|
}
|
|
|
|
const Init *BinOpInit::Fold(const Record *CurRec) const {
|
|
switch (getOpcode()) {
|
|
case CONCAT: {
|
|
const auto *LHSs = dyn_cast<DagInit>(LHS);
|
|
const auto *RHSs = dyn_cast<DagInit>(RHS);
|
|
if (LHSs && RHSs) {
|
|
const auto *LOp = dyn_cast<DefInit>(LHSs->getOperator());
|
|
const auto *ROp = dyn_cast<DefInit>(RHSs->getOperator());
|
|
if ((!LOp && !isa<UnsetInit>(LHSs->getOperator())) ||
|
|
(!ROp && !isa<UnsetInit>(RHSs->getOperator())))
|
|
break;
|
|
if (LOp && ROp && LOp->getDef() != ROp->getDef()) {
|
|
PrintFatalError(Twine("Concatenated Dag operators do not match: '") +
|
|
LHSs->getAsString() + "' vs. '" + RHSs->getAsString() +
|
|
"'");
|
|
}
|
|
const Init *Op = LOp ? LOp : ROp;
|
|
if (!Op)
|
|
Op = UnsetInit::get(getRecordKeeper());
|
|
|
|
SmallVector<const Init *, 8> Args;
|
|
SmallVector<const StringInit *, 8> ArgNames;
|
|
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));
|
|
}
|
|
return DagInit::get(Op, nullptr, Args, ArgNames);
|
|
}
|
|
break;
|
|
}
|
|
case MATCH: {
|
|
const auto *StrInit = dyn_cast<StringInit>(LHS);
|
|
if (!StrInit)
|
|
return this;
|
|
|
|
const auto *RegexInit = dyn_cast<StringInit>(RHS);
|
|
if (!RegexInit)
|
|
return this;
|
|
|
|
StringRef RegexStr = RegexInit->getValue();
|
|
llvm::Regex Matcher(RegexStr);
|
|
if (!Matcher.isValid())
|
|
PrintFatalError(Twine("invalid regex '") + RegexStr + Twine("'"));
|
|
|
|
return BitInit::get(LHS->getRecordKeeper(),
|
|
Matcher.match(StrInit->getValue()));
|
|
}
|
|
case LISTCONCAT: {
|
|
const auto *LHSs = dyn_cast<ListInit>(LHS);
|
|
const auto *RHSs = dyn_cast<ListInit>(RHS);
|
|
if (LHSs && RHSs) {
|
|
SmallVector<const Init *, 8> Args;
|
|
llvm::append_range(Args, *LHSs);
|
|
llvm::append_range(Args, *RHSs);
|
|
return ListInit::get(Args, LHSs->getElementType());
|
|
}
|
|
break;
|
|
}
|
|
case LISTSPLAT: {
|
|
const auto *Value = dyn_cast<TypedInit>(LHS);
|
|
const auto *Size = dyn_cast<IntInit>(RHS);
|
|
if (Value && Size) {
|
|
SmallVector<const Init *, 8> Args(Size->getValue(), Value);
|
|
return ListInit::get(Args, Value->getType());
|
|
}
|
|
break;
|
|
}
|
|
case LISTREMOVE: {
|
|
const auto *LHSs = dyn_cast<ListInit>(LHS);
|
|
const auto *RHSs = dyn_cast<ListInit>(RHS);
|
|
if (LHSs && RHSs) {
|
|
SmallVector<const Init *, 8> Args;
|
|
for (const Init *EltLHS : *LHSs) {
|
|
bool Found = false;
|
|
for (const Init *EltRHS : *RHSs) {
|
|
if (std::optional<bool> Result = CompareInit(EQ, EltLHS, EltRHS)) {
|
|
if (*Result) {
|
|
Found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!Found)
|
|
Args.push_back(EltLHS);
|
|
}
|
|
return ListInit::get(Args, LHSs->getElementType());
|
|
}
|
|
break;
|
|
}
|
|
case LISTELEM: {
|
|
const auto *TheList = dyn_cast<ListInit>(LHS);
|
|
const auto *Idx = dyn_cast<IntInit>(RHS);
|
|
if (!TheList || !Idx)
|
|
break;
|
|
auto i = Idx->getValue();
|
|
if (i < 0 || i >= (ssize_t)TheList->size())
|
|
break;
|
|
return TheList->getElement(i);
|
|
}
|
|
case LISTSLICE: {
|
|
const auto *TheList = dyn_cast<ListInit>(LHS);
|
|
const auto *SliceIdxs = dyn_cast<ListInit>(RHS);
|
|
if (!TheList || !SliceIdxs)
|
|
break;
|
|
SmallVector<const Init *, 8> Args;
|
|
Args.reserve(SliceIdxs->size());
|
|
for (auto *I : *SliceIdxs) {
|
|
auto *II = dyn_cast<IntInit>(I);
|
|
if (!II)
|
|
goto unresolved;
|
|
auto i = II->getValue();
|
|
if (i < 0 || i >= (ssize_t)TheList->size())
|
|
goto unresolved;
|
|
Args.push_back(TheList->getElement(i));
|
|
}
|
|
return ListInit::get(Args, TheList->getElementType());
|
|
}
|
|
case RANGEC: {
|
|
const auto *LHSi = dyn_cast<IntInit>(LHS);
|
|
const auto *RHSi = dyn_cast<IntInit>(RHS);
|
|
if (!LHSi || !RHSi)
|
|
break;
|
|
|
|
auto Start = LHSi->getValue();
|
|
auto End = RHSi->getValue();
|
|
SmallVector<const Init *, 8> Args;
|
|
if (getOpcode() == RANGEC) {
|
|
// Closed interval
|
|
if (Start <= End) {
|
|
// Ascending order
|
|
Args.reserve(End - Start + 1);
|
|
for (auto i = Start; i <= End; ++i)
|
|
Args.push_back(IntInit::get(getRecordKeeper(), i));
|
|
} else {
|
|
// Descending order
|
|
Args.reserve(Start - End + 1);
|
|
for (auto i = Start; i >= End; --i)
|
|
Args.push_back(IntInit::get(getRecordKeeper(), i));
|
|
}
|
|
} else if (Start < End) {
|
|
// Half-open interval (excludes `End`)
|
|
Args.reserve(End - Start);
|
|
for (auto i = Start; i < End; ++i)
|
|
Args.push_back(IntInit::get(getRecordKeeper(), i));
|
|
} else {
|
|
// Empty set
|
|
}
|
|
return ListInit::get(Args, LHSi->getType());
|
|
}
|
|
case STRCONCAT: {
|
|
const auto *LHSs = dyn_cast<StringInit>(LHS);
|
|
const auto *RHSs = dyn_cast<StringInit>(RHS);
|
|
if (LHSs && RHSs)
|
|
return ConcatStringInits(LHSs, RHSs);
|
|
break;
|
|
}
|
|
case INTERLEAVE: {
|
|
const auto *List = dyn_cast<ListInit>(LHS);
|
|
const auto *Delim = dyn_cast<StringInit>(RHS);
|
|
if (List && Delim) {
|
|
const StringInit *Result;
|
|
if (isa<StringRecTy>(List->getElementType()))
|
|
Result = interleaveStringList(List, Delim);
|
|
else
|
|
Result = interleaveIntList(List, Delim);
|
|
if (Result)
|
|
return Result;
|
|
}
|
|
break;
|
|
}
|
|
case EQ:
|
|
case NE:
|
|
case LE:
|
|
case LT:
|
|
case GE:
|
|
case GT: {
|
|
if (std::optional<bool> Result = CompareInit(getOpcode(), LHS, RHS))
|
|
return BitInit::get(getRecordKeeper(), *Result);
|
|
break;
|
|
}
|
|
case GETDAGARG: {
|
|
const auto *Dag = dyn_cast<DagInit>(LHS);
|
|
if (Dag && isa<IntInit, StringInit>(RHS)) {
|
|
std::string Error;
|
|
auto ArgNo = getDagArgNoByKey(Dag, RHS, Error);
|
|
if (!ArgNo)
|
|
PrintFatalError(CurRec->getLoc(), "!getdagarg " + Error);
|
|
|
|
assert(*ArgNo < Dag->getNumArgs());
|
|
|
|
const Init *Arg = Dag->getArg(*ArgNo);
|
|
if (const auto *TI = dyn_cast<TypedInit>(Arg))
|
|
if (!TI->getType()->typeIsConvertibleTo(getType()))
|
|
return UnsetInit::get(Dag->getRecordKeeper());
|
|
return Arg;
|
|
}
|
|
break;
|
|
}
|
|
case GETDAGNAME: {
|
|
const auto *Dag = dyn_cast<DagInit>(LHS);
|
|
const auto *Idx = dyn_cast<IntInit>(RHS);
|
|
if (Dag && Idx) {
|
|
int64_t Pos = Idx->getValue();
|
|
if (Pos < 0 || Pos >= Dag->getNumArgs()) {
|
|
// The index is out-of-range.
|
|
PrintError(CurRec->getLoc(),
|
|
Twine("!getdagname index is out of range 0...") +
|
|
std::to_string(Dag->getNumArgs() - 1) + ": " +
|
|
std::to_string(Pos));
|
|
}
|
|
const Init *ArgName = Dag->getArgName(Pos);
|
|
if (!ArgName)
|
|
return UnsetInit::get(getRecordKeeper());
|
|
return ArgName;
|
|
}
|
|
break;
|
|
}
|
|
case SETDAGOP: {
|
|
const auto *Dag = dyn_cast<DagInit>(LHS);
|
|
const auto *Op = dyn_cast<DefInit>(RHS);
|
|
if (Dag && Op) {
|
|
SmallVector<const Init *, 8> Args;
|
|
SmallVector<const 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;
|
|
}
|
|
case ADD:
|
|
case SUB:
|
|
case MUL:
|
|
case DIV:
|
|
case AND:
|
|
case OR:
|
|
case XOR:
|
|
case SHL:
|
|
case SRA:
|
|
case SRL: {
|
|
const auto *LHSi = dyn_cast_or_null<IntInit>(
|
|
LHS->convertInitializerTo(IntRecTy::get(getRecordKeeper())));
|
|
const auto *RHSi = dyn_cast_or_null<IntInit>(
|
|
RHS->convertInitializerTo(IntRecTy::get(getRecordKeeper())));
|
|
if (LHSi && RHSi) {
|
|
int64_t LHSv = LHSi->getValue(), RHSv = RHSi->getValue();
|
|
int64_t Result;
|
|
switch (getOpcode()) {
|
|
default: llvm_unreachable("Bad opcode!");
|
|
case ADD: Result = LHSv + RHSv; break;
|
|
case SUB: Result = LHSv - RHSv; break;
|
|
case MUL: Result = LHSv * RHSv; break;
|
|
case DIV:
|
|
if (RHSv == 0)
|
|
PrintFatalError(CurRec->getLoc(),
|
|
"Illegal operation: division by zero");
|
|
else if (LHSv == INT64_MIN && RHSv == -1)
|
|
PrintFatalError(CurRec->getLoc(),
|
|
"Illegal operation: INT64_MIN / -1");
|
|
else
|
|
Result = LHSv / RHSv;
|
|
break;
|
|
case AND: Result = LHSv & RHSv; break;
|
|
case OR: Result = LHSv | RHSv; break;
|
|
case XOR: Result = LHSv ^ RHSv; break;
|
|
case SHL: Result = (uint64_t)LHSv << (uint64_t)RHSv; break;
|
|
case SRA: Result = LHSv >> RHSv; break;
|
|
case SRL: Result = (uint64_t)LHSv >> (uint64_t)RHSv; break;
|
|
}
|
|
return IntInit::get(getRecordKeeper(), Result);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
unresolved:
|
|
return this;
|
|
}
|
|
|
|
const Init *BinOpInit::resolveReferences(Resolver &R) const {
|
|
const Init *lhs = LHS->resolveReferences(R);
|
|
const Init *rhs = RHS->resolveReferences(R);
|
|
|
|
unsigned Opc = getOpcode();
|
|
if (Opc == AND || Opc == OR) {
|
|
// Short-circuit. Regardless whether this is a logical or bitwise
|
|
// AND/OR.
|
|
// Ideally we could also short-circuit `!or(true, ...)`, but it's
|
|
// difficult to do it right without knowing if rest of the operands
|
|
// are all `bit` or not. Therefore, we're only implementing a relatively
|
|
// limited version of short-circuit against all ones (`true` is casted
|
|
// to 1 rather than all ones before we evaluate `!or`).
|
|
if (const auto *LHSi = dyn_cast_or_null<IntInit>(
|
|
lhs->convertInitializerTo(IntRecTy::get(getRecordKeeper())))) {
|
|
if ((Opc == AND && !LHSi->getValue()) ||
|
|
(Opc == OR && LHSi->getValue() == -1))
|
|
return LHSi;
|
|
}
|
|
}
|
|
|
|
if (LHS != lhs || RHS != rhs)
|
|
return (BinOpInit::get(getOpcode(), lhs, rhs, getType()))
|
|
->Fold(R.getCurrentRecord());
|
|
return this;
|
|
}
|
|
|
|
std::string BinOpInit::getAsString() const {
|
|
std::string Result;
|
|
switch (getOpcode()) {
|
|
case LISTELEM:
|
|
case LISTSLICE:
|
|
return LHS->getAsString() + "[" + RHS->getAsString() + "]";
|
|
case RANGEC:
|
|
return LHS->getAsString() + "..." + RHS->getAsString();
|
|
case CONCAT: Result = "!con"; break;
|
|
case MATCH:
|
|
Result = "!match";
|
|
break;
|
|
case ADD: Result = "!add"; break;
|
|
case SUB: Result = "!sub"; break;
|
|
case MUL: Result = "!mul"; break;
|
|
case DIV: Result = "!div"; break;
|
|
case AND: Result = "!and"; break;
|
|
case OR: Result = "!or"; break;
|
|
case XOR: Result = "!xor"; break;
|
|
case SHL: Result = "!shl"; break;
|
|
case SRA: Result = "!sra"; break;
|
|
case SRL: Result = "!srl"; break;
|
|
case EQ: Result = "!eq"; break;
|
|
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;
|
|
case LISTCONCAT: Result = "!listconcat"; break;
|
|
case LISTSPLAT: Result = "!listsplat"; break;
|
|
case LISTREMOVE:
|
|
Result = "!listremove";
|
|
break;
|
|
case STRCONCAT: Result = "!strconcat"; break;
|
|
case INTERLEAVE: Result = "!interleave"; break;
|
|
case SETDAGOP: Result = "!setdagop"; break;
|
|
case GETDAGARG:
|
|
Result = "!getdagarg<" + getType()->getAsString() + ">";
|
|
break;
|
|
case GETDAGNAME:
|
|
Result = "!getdagname";
|
|
break;
|
|
}
|
|
return Result + "(" + LHS->getAsString() + ", " + RHS->getAsString() + ")";
|
|
}
|
|
|
|
static void ProfileTernOpInit(FoldingSetNodeID &ID, unsigned Opcode,
|
|
const Init *LHS, const Init *MHS, const Init *RHS,
|
|
const RecTy *Type) {
|
|
ID.AddInteger(Opcode);
|
|
ID.AddPointer(LHS);
|
|
ID.AddPointer(MHS);
|
|
ID.AddPointer(RHS);
|
|
ID.AddPointer(Type);
|
|
}
|
|
|
|
const TernOpInit *TernOpInit::get(TernaryOp Opc, const Init *LHS,
|
|
const Init *MHS, const Init *RHS,
|
|
const RecTy *Type) {
|
|
FoldingSetNodeID ID;
|
|
ProfileTernOpInit(ID, Opc, LHS, MHS, RHS, Type);
|
|
|
|
detail::RecordKeeperImpl &RK = LHS->getRecordKeeper().getImpl();
|
|
void *IP = nullptr;
|
|
if (TernOpInit *I = RK.TheTernOpInitPool.FindNodeOrInsertPos(ID, IP))
|
|
return I;
|
|
|
|
TernOpInit *I = new (RK.Allocator) TernOpInit(Opc, LHS, MHS, RHS, Type);
|
|
RK.TheTernOpInitPool.InsertNode(I, IP);
|
|
return I;
|
|
}
|
|
|
|
void TernOpInit::Profile(FoldingSetNodeID &ID) const {
|
|
ProfileTernOpInit(ID, getOpcode(), getLHS(), getMHS(), getRHS(), getType());
|
|
}
|
|
|
|
static const Init *ItemApply(const Init *LHS, const Init *MHSe, const Init *RHS,
|
|
const Record *CurRec) {
|
|
MapResolver R(CurRec);
|
|
R.set(LHS, MHSe);
|
|
return RHS->resolveReferences(R);
|
|
}
|
|
|
|
static const Init *ForeachDagApply(const Init *LHS, const DagInit *MHSd,
|
|
const Init *RHS, const Record *CurRec) {
|
|
bool Change = false;
|
|
const Init *Val = ItemApply(LHS, MHSd->getOperator(), RHS, CurRec);
|
|
if (Val != MHSd->getOperator())
|
|
Change = true;
|
|
|
|
SmallVector<std::pair<const Init *, const StringInit *>, 8> NewArgs;
|
|
for (unsigned int i = 0; i < MHSd->getNumArgs(); ++i) {
|
|
const Init *Arg = MHSd->getArg(i);
|
|
const Init *NewArg;
|
|
const StringInit *ArgName = MHSd->getArgName(i);
|
|
|
|
if (const auto *Argd = dyn_cast<DagInit>(Arg))
|
|
NewArg = ForeachDagApply(LHS, Argd, RHS, CurRec);
|
|
else
|
|
NewArg = ItemApply(LHS, Arg, RHS, CurRec);
|
|
|
|
NewArgs.emplace_back(NewArg, ArgName);
|
|
if (Arg != NewArg)
|
|
Change = true;
|
|
}
|
|
|
|
if (Change)
|
|
return DagInit::get(Val, nullptr, NewArgs);
|
|
return MHSd;
|
|
}
|
|
|
|
// Applies RHS to all elements of MHS, using LHS as a temp variable.
|
|
static const Init *ForeachHelper(const Init *LHS, const Init *MHS,
|
|
const Init *RHS, const RecTy *Type,
|
|
const Record *CurRec) {
|
|
if (const auto *MHSd = dyn_cast<DagInit>(MHS))
|
|
return ForeachDagApply(LHS, MHSd, RHS, CurRec);
|
|
|
|
if (const auto *MHSl = dyn_cast<ListInit>(MHS)) {
|
|
SmallVector<const Init *, 8> NewList(MHSl->begin(), MHSl->end());
|
|
|
|
for (const Init *&Item : NewList) {
|
|
const Init *NewItem = ItemApply(LHS, Item, RHS, CurRec);
|
|
if (NewItem != Item)
|
|
Item = NewItem;
|
|
}
|
|
return ListInit::get(NewList, cast<ListRecTy>(Type)->getElementType());
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// 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 const Init *FilterHelper(const Init *LHS, const Init *MHS,
|
|
const Init *RHS, const RecTy *Type,
|
|
const Record *CurRec) {
|
|
if (const auto *MHSl = dyn_cast<ListInit>(MHS)) {
|
|
SmallVector<const Init *, 8> NewList;
|
|
|
|
for (const Init *Item : MHSl->getValues()) {
|
|
const Init *Include = ItemApply(LHS, Item, RHS, CurRec);
|
|
if (!Include)
|
|
return nullptr;
|
|
if (const auto *IncludeInt =
|
|
dyn_cast_or_null<IntInit>(Include->convertInitializerTo(
|
|
IntRecTy::get(LHS->getRecordKeeper())))) {
|
|
if (IncludeInt->getValue())
|
|
NewList.push_back(Item);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
return ListInit::get(NewList, cast<ListRecTy>(Type)->getElementType());
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
const Init *TernOpInit::Fold(const Record *CurRec) const {
|
|
RecordKeeper &RK = getRecordKeeper();
|
|
switch (getOpcode()) {
|
|
case SUBST: {
|
|
const auto *LHSd = dyn_cast<DefInit>(LHS);
|
|
const auto *LHSv = dyn_cast<VarInit>(LHS);
|
|
const auto *LHSs = dyn_cast<StringInit>(LHS);
|
|
|
|
const auto *MHSd = dyn_cast<DefInit>(MHS);
|
|
const auto *MHSv = dyn_cast<VarInit>(MHS);
|
|
const auto *MHSs = dyn_cast<StringInit>(MHS);
|
|
|
|
const auto *RHSd = dyn_cast<DefInit>(RHS);
|
|
const auto *RHSv = dyn_cast<VarInit>(RHS);
|
|
const auto *RHSs = dyn_cast<StringInit>(RHS);
|
|
|
|
if (LHSd && MHSd && RHSd) {
|
|
const Record *Val = RHSd->getDef();
|
|
if (LHSd->getAsString() == RHSd->getAsString())
|
|
Val = MHSd->getDef();
|
|
return Val->getDefInit();
|
|
}
|
|
if (LHSv && MHSv && RHSv) {
|
|
std::string Val = std::string(RHSv->getName());
|
|
if (LHSv->getAsString() == RHSv->getAsString())
|
|
Val = std::string(MHSv->getName());
|
|
return VarInit::get(Val, getType());
|
|
}
|
|
if (LHSs && MHSs && RHSs) {
|
|
std::string Val = std::string(RHSs->getValue());
|
|
|
|
std::string::size_type found;
|
|
std::string::size_type idx = 0;
|
|
while (true) {
|
|
found = Val.find(std::string(LHSs->getValue()), idx);
|
|
if (found == std::string::npos)
|
|
break;
|
|
Val.replace(found, LHSs->getValue().size(),
|
|
std::string(MHSs->getValue()));
|
|
idx = found + MHSs->getValue().size();
|
|
}
|
|
|
|
return StringInit::get(RK, Val);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FOREACH: {
|
|
if (const Init *Result = ForeachHelper(LHS, MHS, RHS, getType(), CurRec))
|
|
return Result;
|
|
break;
|
|
}
|
|
|
|
case FILTER: {
|
|
if (const Init *Result = FilterHelper(LHS, MHS, RHS, getType(), CurRec))
|
|
return Result;
|
|
break;
|
|
}
|
|
|
|
case IF: {
|
|
if (const auto *LHSi = dyn_cast_or_null<IntInit>(
|
|
LHS->convertInitializerTo(IntRecTy::get(RK)))) {
|
|
if (LHSi->getValue())
|
|
return MHS;
|
|
return RHS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DAG: {
|
|
const auto *MHSl = dyn_cast<ListInit>(MHS);
|
|
const auto *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<const Init *, const StringInit *>, 8> Children;
|
|
unsigned Size = MHSl ? MHSl->size() : RHSl->size();
|
|
for (unsigned i = 0; i != Size; ++i) {
|
|
const Init *Node = MHSl ? MHSl->getElement(i) : UnsetInit::get(RK);
|
|
const Init *Name = RHSl ? RHSl->getElement(i) : UnsetInit::get(RK);
|
|
if (!isa<StringInit>(Name) && !isa<UnsetInit>(Name))
|
|
return this;
|
|
Children.emplace_back(Node, dyn_cast<StringInit>(Name));
|
|
}
|
|
return DagInit::get(LHS, nullptr, Children);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RANGE: {
|
|
const auto *LHSi = dyn_cast<IntInit>(LHS);
|
|
const auto *MHSi = dyn_cast<IntInit>(MHS);
|
|
const auto *RHSi = dyn_cast<IntInit>(RHS);
|
|
if (!LHSi || !MHSi || !RHSi)
|
|
break;
|
|
|
|
auto Start = LHSi->getValue();
|
|
auto End = MHSi->getValue();
|
|
auto Step = RHSi->getValue();
|
|
if (Step == 0)
|
|
PrintError(CurRec->getLoc(), "Step of !range can't be 0");
|
|
|
|
SmallVector<const Init *, 8> Args;
|
|
if (Start < End && Step > 0) {
|
|
Args.reserve((End - Start) / Step);
|
|
for (auto I = Start; I < End; I += Step)
|
|
Args.push_back(IntInit::get(getRecordKeeper(), I));
|
|
} else if (Start > End && Step < 0) {
|
|
Args.reserve((Start - End) / -Step);
|
|
for (auto I = Start; I > End; I += Step)
|
|
Args.push_back(IntInit::get(getRecordKeeper(), I));
|
|
} else {
|
|
// Empty set
|
|
}
|
|
return ListInit::get(Args, LHSi->getType());
|
|
}
|
|
|
|
case SUBSTR: {
|
|
const auto *LHSs = dyn_cast<StringInit>(LHS);
|
|
const auto *MHSi = dyn_cast<IntInit>(MHS);
|
|
const auto *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(RK, LHSs->getValue().substr(Start, Length),
|
|
LHSs->getFormat());
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FIND: {
|
|
const auto *LHSs = dyn_cast<StringInit>(LHS);
|
|
const auto *MHSs = dyn_cast<StringInit>(MHS);
|
|
const auto *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(RK, -1);
|
|
return IntInit::get(RK, I);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SETDAGARG: {
|
|
const auto *Dag = dyn_cast<DagInit>(LHS);
|
|
if (Dag && isa<IntInit, StringInit>(MHS)) {
|
|
std::string Error;
|
|
auto ArgNo = getDagArgNoByKey(Dag, MHS, Error);
|
|
if (!ArgNo)
|
|
PrintFatalError(CurRec->getLoc(), "!setdagarg " + Error);
|
|
|
|
assert(*ArgNo < Dag->getNumArgs());
|
|
|
|
SmallVector<const Init *, 8> Args(Dag->getArgs());
|
|
SmallVector<const StringInit *, 8> Names(Dag->getArgNames());
|
|
Args[*ArgNo] = RHS;
|
|
return DagInit::get(Dag->getOperator(), Dag->getName(), Args, Names);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SETDAGNAME: {
|
|
const auto *Dag = dyn_cast<DagInit>(LHS);
|
|
if (Dag && isa<IntInit, StringInit>(MHS)) {
|
|
std::string Error;
|
|
auto ArgNo = getDagArgNoByKey(Dag, MHS, Error);
|
|
if (!ArgNo)
|
|
PrintFatalError(CurRec->getLoc(), "!setdagname " + Error);
|
|
|
|
assert(*ArgNo < Dag->getNumArgs());
|
|
|
|
SmallVector<const Init *, 8> Args(Dag->getArgs());
|
|
SmallVector<const StringInit *, 8> Names(Dag->getArgNames());
|
|
Names[*ArgNo] = dyn_cast<StringInit>(RHS);
|
|
return DagInit::get(Dag->getOperator(), Dag->getName(), Args, Names);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
const Init *TernOpInit::resolveReferences(Resolver &R) const {
|
|
const Init *lhs = LHS->resolveReferences(R);
|
|
|
|
if (getOpcode() == IF && lhs != LHS) {
|
|
if (const auto *Value = dyn_cast_or_null<IntInit>(
|
|
lhs->convertInitializerTo(IntRecTy::get(getRecordKeeper())))) {
|
|
// Short-circuit
|
|
if (Value->getValue())
|
|
return MHS->resolveReferences(R);
|
|
return RHS->resolveReferences(R);
|
|
}
|
|
}
|
|
|
|
const Init *mhs = MHS->resolveReferences(R);
|
|
const Init *rhs;
|
|
|
|
if (getOpcode() == FOREACH || getOpcode() == FILTER) {
|
|
ShadowResolver SR(R);
|
|
SR.addShadow(lhs);
|
|
rhs = RHS->resolveReferences(SR);
|
|
} else {
|
|
rhs = RHS->resolveReferences(R);
|
|
}
|
|
|
|
if (LHS != lhs || MHS != mhs || RHS != rhs)
|
|
return (TernOpInit::get(getOpcode(), lhs, mhs, rhs, getType()))
|
|
->Fold(R.getCurrentRecord());
|
|
return this;
|
|
}
|
|
|
|
std::string TernOpInit::getAsString() const {
|
|
std::string Result;
|
|
bool UnquotedLHS = false;
|
|
switch (getOpcode()) {
|
|
case DAG: Result = "!dag"; break;
|
|
case FILTER: Result = "!filter"; UnquotedLHS = true; break;
|
|
case FOREACH: Result = "!foreach"; UnquotedLHS = true; break;
|
|
case IF: Result = "!if"; break;
|
|
case RANGE:
|
|
Result = "!range";
|
|
break;
|
|
case SUBST: Result = "!subst"; break;
|
|
case SUBSTR: Result = "!substr"; break;
|
|
case FIND: Result = "!find"; break;
|
|
case SETDAGARG:
|
|
Result = "!setdagarg";
|
|
break;
|
|
case SETDAGNAME:
|
|
Result = "!setdagname";
|
|
break;
|
|
}
|
|
return (Result + "(" +
|
|
(UnquotedLHS ? LHS->getAsUnquotedString() : LHS->getAsString()) +
|
|
", " + MHS->getAsString() + ", " + RHS->getAsString() + ")");
|
|
}
|
|
|
|
static void ProfileFoldOpInit(FoldingSetNodeID &ID, const Init *Start,
|
|
const Init *List, const Init *A, const Init *B,
|
|
const Init *Expr, const RecTy *Type) {
|
|
ID.AddPointer(Start);
|
|
ID.AddPointer(List);
|
|
ID.AddPointer(A);
|
|
ID.AddPointer(B);
|
|
ID.AddPointer(Expr);
|
|
ID.AddPointer(Type);
|
|
}
|
|
|
|
const FoldOpInit *FoldOpInit::get(const Init *Start, const Init *List,
|
|
const Init *A, const Init *B,
|
|
const Init *Expr, const RecTy *Type) {
|
|
FoldingSetNodeID ID;
|
|
ProfileFoldOpInit(ID, Start, List, A, B, Expr, Type);
|
|
|
|
detail::RecordKeeperImpl &RK = Start->getRecordKeeper().getImpl();
|
|
void *IP = nullptr;
|
|
if (const FoldOpInit *I = RK.TheFoldOpInitPool.FindNodeOrInsertPos(ID, IP))
|
|
return I;
|
|
|
|
FoldOpInit *I = new (RK.Allocator) FoldOpInit(Start, List, A, B, Expr, Type);
|
|
RK.TheFoldOpInitPool.InsertNode(I, IP);
|
|
return I;
|
|
}
|
|
|
|
void FoldOpInit::Profile(FoldingSetNodeID &ID) const {
|
|
ProfileFoldOpInit(ID, Start, List, A, B, Expr, getType());
|
|
}
|
|
|
|
const Init *FoldOpInit::Fold(const Record *CurRec) const {
|
|
if (const auto *LI = dyn_cast<ListInit>(List)) {
|
|
const Init *Accum = Start;
|
|
for (const Init *Elt : *LI) {
|
|
MapResolver R(CurRec);
|
|
R.set(A, Accum);
|
|
R.set(B, Elt);
|
|
Accum = Expr->resolveReferences(R);
|
|
}
|
|
return Accum;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
const Init *FoldOpInit::resolveReferences(Resolver &R) const {
|
|
const Init *NewStart = Start->resolveReferences(R);
|
|
const Init *NewList = List->resolveReferences(R);
|
|
ShadowResolver SR(R);
|
|
SR.addShadow(A);
|
|
SR.addShadow(B);
|
|
const Init *NewExpr = Expr->resolveReferences(SR);
|
|
|
|
if (Start == NewStart && List == NewList && Expr == NewExpr)
|
|
return this;
|
|
|
|
return get(NewStart, NewList, A, B, NewExpr, getType())
|
|
->Fold(R.getCurrentRecord());
|
|
}
|
|
|
|
const Init *FoldOpInit::getBit(unsigned Bit) const {
|
|
return VarBitInit::get(this, Bit);
|
|
}
|
|
|
|
std::string FoldOpInit::getAsString() const {
|
|
return (Twine("!foldl(") + Start->getAsString() + ", " + List->getAsString() +
|
|
", " + A->getAsUnquotedString() + ", " + B->getAsUnquotedString() +
|
|
", " + Expr->getAsString() + ")")
|
|
.str();
|
|
}
|
|
|
|
static void ProfileIsAOpInit(FoldingSetNodeID &ID, const RecTy *CheckType,
|
|
const Init *Expr) {
|
|
ID.AddPointer(CheckType);
|
|
ID.AddPointer(Expr);
|
|
}
|
|
|
|
const IsAOpInit *IsAOpInit::get(const RecTy *CheckType, const Init *Expr) {
|
|
|
|
FoldingSetNodeID ID;
|
|
ProfileIsAOpInit(ID, CheckType, Expr);
|
|
|
|
detail::RecordKeeperImpl &RK = Expr->getRecordKeeper().getImpl();
|
|
void *IP = nullptr;
|
|
if (const IsAOpInit *I = RK.TheIsAOpInitPool.FindNodeOrInsertPos(ID, IP))
|
|
return I;
|
|
|
|
IsAOpInit *I = new (RK.Allocator) IsAOpInit(CheckType, Expr);
|
|
RK.TheIsAOpInitPool.InsertNode(I, IP);
|
|
return I;
|
|
}
|
|
|
|
void IsAOpInit::Profile(FoldingSetNodeID &ID) const {
|
|
ProfileIsAOpInit(ID, CheckType, Expr);
|
|
}
|
|
|
|
const Init *IsAOpInit::Fold() const {
|
|
if (const auto *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(getRecordKeeper(), 1);
|
|
|
|
if (isa<RecordRecTy>(CheckType)) {
|
|
// If the target type is not a subclass of the expression type once the
|
|
// expression has been made concrete, 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()) &&
|
|
Expr->isConcrete()) ||
|
|
isa<DefInit>(Expr))
|
|
return IntInit::get(getRecordKeeper(), 0);
|
|
} else {
|
|
// We treat non-record types as not castable.
|
|
return IntInit::get(getRecordKeeper(), 0);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
const Init *IsAOpInit::resolveReferences(Resolver &R) const {
|
|
const Init *NewExpr = Expr->resolveReferences(R);
|
|
if (Expr != NewExpr)
|
|
return get(CheckType, NewExpr)->Fold();
|
|
return this;
|
|
}
|
|
|
|
const Init *IsAOpInit::getBit(unsigned Bit) const {
|
|
return VarBitInit::get(this, Bit);
|
|
}
|
|
|
|
std::string IsAOpInit::getAsString() const {
|
|
return (Twine("!isa<") + CheckType->getAsString() + ">(" +
|
|
Expr->getAsString() + ")")
|
|
.str();
|
|
}
|
|
|
|
static void ProfileExistsOpInit(FoldingSetNodeID &ID, const RecTy *CheckType,
|
|
const Init *Expr) {
|
|
ID.AddPointer(CheckType);
|
|
ID.AddPointer(Expr);
|
|
}
|
|
|
|
const ExistsOpInit *ExistsOpInit::get(const RecTy *CheckType,
|
|
const Init *Expr) {
|
|
FoldingSetNodeID ID;
|
|
ProfileExistsOpInit(ID, CheckType, Expr);
|
|
|
|
detail::RecordKeeperImpl &RK = Expr->getRecordKeeper().getImpl();
|
|
void *IP = nullptr;
|
|
if (const ExistsOpInit *I =
|
|
RK.TheExistsOpInitPool.FindNodeOrInsertPos(ID, IP))
|
|
return I;
|
|
|
|
ExistsOpInit *I = new (RK.Allocator) ExistsOpInit(CheckType, Expr);
|
|
RK.TheExistsOpInitPool.InsertNode(I, IP);
|
|
return I;
|
|
}
|
|
|
|
void ExistsOpInit::Profile(FoldingSetNodeID &ID) const {
|
|
ProfileExistsOpInit(ID, CheckType, Expr);
|
|
}
|
|
|
|
const Init *ExistsOpInit::Fold(const Record *CurRec, bool IsFinal) const {
|
|
if (const auto *Name = dyn_cast<StringInit>(Expr)) {
|
|
// Look up all defined records to see if we can find one.
|
|
const Record *D = CheckType->getRecordKeeper().getDef(Name->getValue());
|
|
if (D) {
|
|
// Check if types are compatible.
|
|
return IntInit::get(getRecordKeeper(),
|
|
D->getDefInit()->getType()->typeIsA(CheckType));
|
|
}
|
|
|
|
if (CurRec) {
|
|
// Self-references are allowed, but their resolution is delayed until
|
|
// the final resolve to ensure that we get the correct type for them.
|
|
auto *Anonymous = dyn_cast<AnonymousNameInit>(CurRec->getNameInit());
|
|
if (Name == CurRec->getNameInit() ||
|
|
(Anonymous && Name == Anonymous->getNameInit())) {
|
|
if (!IsFinal)
|
|
return this;
|
|
|
|
// No doubt that there exists a record, so we should check if types are
|
|
// compatible.
|
|
return IntInit::get(getRecordKeeper(),
|
|
CurRec->getType()->typeIsA(CheckType));
|
|
}
|
|
}
|
|
|
|
if (IsFinal)
|
|
return IntInit::get(getRecordKeeper(), 0);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
const Init *ExistsOpInit::resolveReferences(Resolver &R) const {
|
|
const Init *NewExpr = Expr->resolveReferences(R);
|
|
if (Expr != NewExpr || R.isFinal())
|
|
return get(CheckType, NewExpr)->Fold(R.getCurrentRecord(), R.isFinal());
|
|
return this;
|
|
}
|
|
|
|
const Init *ExistsOpInit::getBit(unsigned Bit) const {
|
|
return VarBitInit::get(this, Bit);
|
|
}
|
|
|
|
std::string ExistsOpInit::getAsString() const {
|
|
return (Twine("!exists<") + CheckType->getAsString() + ">(" +
|
|
Expr->getAsString() + ")")
|
|
.str();
|
|
}
|
|
|
|
const RecTy *TypedInit::getFieldType(const StringInit *FieldName) const {
|
|
if (const auto *RecordType = dyn_cast<RecordRecTy>(getType())) {
|
|
for (const Record *Rec : RecordType->getClasses()) {
|
|
if (const RecordVal *Field = Rec->getValue(FieldName))
|
|
return Field->getType();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const Init *TypedInit::convertInitializerTo(const RecTy *Ty) const {
|
|
if (getType() == Ty || getType()->typeIsA(Ty))
|
|
return this;
|
|
|
|
if (isa<BitRecTy>(getType()) && isa<BitsRecTy>(Ty) &&
|
|
cast<BitsRecTy>(Ty)->getNumBits() == 1)
|
|
return BitsInit::get(getRecordKeeper(), {this});
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
const Init *
|
|
TypedInit::convertInitializerBitRange(ArrayRef<unsigned> Bits) const {
|
|
const auto *T = dyn_cast<BitsRecTy>(getType());
|
|
if (!T) return nullptr; // Cannot subscript a non-bits variable.
|
|
unsigned NumBits = T->getNumBits();
|
|
|
|
SmallVector<const Init *, 16> NewBits;
|
|
NewBits.reserve(Bits.size());
|
|
for (unsigned Bit : Bits) {
|
|
if (Bit >= NumBits)
|
|
return nullptr;
|
|
|
|
NewBits.push_back(VarBitInit::get(this, Bit));
|
|
}
|
|
return BitsInit::get(getRecordKeeper(), NewBits);
|
|
}
|
|
|
|
const Init *TypedInit::getCastTo(const RecTy *Ty) const {
|
|
// Handle the common case quickly
|
|
if (getType() == Ty || getType()->typeIsA(Ty))
|
|
return this;
|
|
|
|
if (const 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, this, Ty)->Fold(nullptr);
|
|
}
|
|
|
|
const VarInit *VarInit::get(StringRef VN, const RecTy *T) {
|
|
const Init *Value = StringInit::get(T->getRecordKeeper(), VN);
|
|
return VarInit::get(Value, T);
|
|
}
|
|
|
|
const VarInit *VarInit::get(const Init *VN, const RecTy *T) {
|
|
detail::RecordKeeperImpl &RK = T->getRecordKeeper().getImpl();
|
|
VarInit *&I = RK.TheVarInitPool[{T, VN}];
|
|
if (!I)
|
|
I = new (RK.Allocator) VarInit(VN, T);
|
|
return I;
|
|
}
|
|
|
|
StringRef VarInit::getName() const {
|
|
const auto *NameString = cast<StringInit>(getNameInit());
|
|
return NameString->getValue();
|
|
}
|
|
|
|
const Init *VarInit::getBit(unsigned Bit) const {
|
|
if (getType() == BitRecTy::get(getRecordKeeper()))
|
|
return this;
|
|
return VarBitInit::get(this, Bit);
|
|
}
|
|
|
|
const Init *VarInit::resolveReferences(Resolver &R) const {
|
|
if (const Init *Val = R.resolve(VarName))
|
|
return Val;
|
|
return this;
|
|
}
|
|
|
|
const VarBitInit *VarBitInit::get(const TypedInit *T, unsigned B) {
|
|
detail::RecordKeeperImpl &RK = T->getRecordKeeper().getImpl();
|
|
VarBitInit *&I = RK.TheVarBitInitPool[{T, B}];
|
|
if (!I)
|
|
I = new (RK.Allocator) VarBitInit(T, B);
|
|
return I;
|
|
}
|
|
|
|
std::string VarBitInit::getAsString() const {
|
|
return TI->getAsString() + "{" + utostr(Bit) + "}";
|
|
}
|
|
|
|
const Init *VarBitInit::resolveReferences(Resolver &R) const {
|
|
const Init *I = TI->resolveReferences(R);
|
|
if (TI != I)
|
|
return I->getBit(getBitNum());
|
|
|
|
return this;
|
|
}
|
|
|
|
DefInit::DefInit(const Record *D)
|
|
: TypedInit(IK_DefInit, D->getType()), Def(D) {}
|
|
|
|
const Init *DefInit::convertInitializerTo(const RecTy *Ty) const {
|
|
if (auto *RRT = dyn_cast<RecordRecTy>(Ty))
|
|
if (getType()->typeIsConvertibleTo(RRT))
|
|
return this;
|
|
return nullptr;
|
|
}
|
|
|
|
const RecTy *DefInit::getFieldType(const StringInit *FieldName) const {
|
|
if (const RecordVal *RV = Def->getValue(FieldName))
|
|
return RV->getType();
|
|
return nullptr;
|
|
}
|
|
|
|
std::string DefInit::getAsString() const { return std::string(Def->getName()); }
|
|
|
|
static void ProfileVarDefInit(FoldingSetNodeID &ID, const Record *Class,
|
|
ArrayRef<const ArgumentInit *> Args) {
|
|
ID.AddInteger(Args.size());
|
|
ID.AddPointer(Class);
|
|
|
|
for (const Init *I : Args)
|
|
ID.AddPointer(I);
|
|
}
|
|
|
|
VarDefInit::VarDefInit(SMLoc Loc, const Record *Class, unsigned N)
|
|
: TypedInit(IK_VarDefInit, RecordRecTy::get(Class)), Loc(Loc), Class(Class),
|
|
NumArgs(N) {}
|
|
|
|
const VarDefInit *VarDefInit::get(SMLoc Loc, const Record *Class,
|
|
ArrayRef<const ArgumentInit *> Args) {
|
|
FoldingSetNodeID ID;
|
|
ProfileVarDefInit(ID, Class, Args);
|
|
|
|
detail::RecordKeeperImpl &RK = Class->getRecords().getImpl();
|
|
void *IP = nullptr;
|
|
if (const VarDefInit *I = RK.TheVarDefInitPool.FindNodeOrInsertPos(ID, IP))
|
|
return I;
|
|
|
|
void *Mem = RK.Allocator.Allocate(
|
|
totalSizeToAlloc<const ArgumentInit *>(Args.size()), alignof(VarDefInit));
|
|
VarDefInit *I = new (Mem) VarDefInit(Loc, Class, Args.size());
|
|
std::uninitialized_copy(Args.begin(), Args.end(),
|
|
I->getTrailingObjects<const ArgumentInit *>());
|
|
RK.TheVarDefInitPool.InsertNode(I, IP);
|
|
return I;
|
|
}
|
|
|
|
void VarDefInit::Profile(FoldingSetNodeID &ID) const {
|
|
ProfileVarDefInit(ID, Class, args());
|
|
}
|
|
|
|
const DefInit *VarDefInit::instantiate() {
|
|
if (Def)
|
|
return Def;
|
|
|
|
RecordKeeper &Records = Class->getRecords();
|
|
auto NewRecOwner = std::make_unique<Record>(
|
|
Records.getNewAnonymousName(), Loc, Records, Record::RK_AnonymousDef);
|
|
Record *NewRec = NewRecOwner.get();
|
|
|
|
// Copy values from class to instance
|
|
for (const RecordVal &Val : Class->getValues())
|
|
NewRec->addValue(Val);
|
|
|
|
// Copy assertions from class to instance.
|
|
NewRec->appendAssertions(Class);
|
|
|
|
// Copy dumps from class to instance.
|
|
NewRec->appendDumps(Class);
|
|
|
|
// Substitute and resolve template arguments
|
|
ArrayRef<const Init *> TArgs = Class->getTemplateArgs();
|
|
MapResolver R(NewRec);
|
|
|
|
for (const Init *Arg : TArgs) {
|
|
R.set(Arg, NewRec->getValue(Arg)->getValue());
|
|
NewRec->removeValue(Arg);
|
|
}
|
|
|
|
for (auto *Arg : args()) {
|
|
if (Arg->isPositional())
|
|
R.set(TArgs[Arg->getIndex()], Arg->getValue());
|
|
if (Arg->isNamed())
|
|
R.set(Arg->getName(), Arg->getValue());
|
|
}
|
|
|
|
NewRec->resolveReferences(R);
|
|
|
|
// Add superclasses.
|
|
for (const auto &[SC, Loc] : Class->getSuperClasses())
|
|
NewRec->addSuperClass(SC, Loc);
|
|
|
|
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));
|
|
|
|
// Check the assertions.
|
|
NewRec->checkRecordAssertions();
|
|
|
|
// Check the assertions.
|
|
NewRec->emitRecordDumps();
|
|
|
|
return Def = NewRec->getDefInit();
|
|
}
|
|
|
|
const Init *VarDefInit::resolveReferences(Resolver &R) const {
|
|
TrackUnresolvedResolver UR(&R);
|
|
bool Changed = false;
|
|
SmallVector<const ArgumentInit *, 8> NewArgs;
|
|
NewArgs.reserve(args_size());
|
|
|
|
for (const ArgumentInit *Arg : args()) {
|
|
const auto *NewArg = cast<ArgumentInit>(Arg->resolveReferences(UR));
|
|
NewArgs.push_back(NewArg);
|
|
Changed |= NewArg != Arg;
|
|
}
|
|
|
|
if (Changed) {
|
|
auto *New = VarDefInit::get(Loc, Class, NewArgs);
|
|
if (!UR.foundUnresolved())
|
|
return const_cast<VarDefInit *>(New)->instantiate();
|
|
return New;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
const Init *VarDefInit::Fold() const {
|
|
if (Def)
|
|
return Def;
|
|
|
|
TrackUnresolvedResolver R;
|
|
for (const Init *Arg : args())
|
|
Arg->resolveReferences(R);
|
|
|
|
if (!R.foundUnresolved())
|
|
return const_cast<VarDefInit *>(this)->instantiate();
|
|
return this;
|
|
}
|
|
|
|
std::string VarDefInit::getAsString() const {
|
|
std::string Result = Class->getNameInitAsString() + "<";
|
|
const char *sep = "";
|
|
for (const Init *Arg : args()) {
|
|
Result += sep;
|
|
sep = ", ";
|
|
Result += Arg->getAsString();
|
|
}
|
|
return Result + ">";
|
|
}
|
|
|
|
const FieldInit *FieldInit::get(const Init *R, const StringInit *FN) {
|
|
detail::RecordKeeperImpl &RK = R->getRecordKeeper().getImpl();
|
|
FieldInit *&I = RK.TheFieldInitPool[{R, FN}];
|
|
if (!I)
|
|
I = new (RK.Allocator) FieldInit(R, FN);
|
|
return I;
|
|
}
|
|
|
|
const Init *FieldInit::getBit(unsigned Bit) const {
|
|
if (getType() == BitRecTy::get(getRecordKeeper()))
|
|
return this;
|
|
return VarBitInit::get(this, Bit);
|
|
}
|
|
|
|
const Init *FieldInit::resolveReferences(Resolver &R) const {
|
|
const Init *NewRec = Rec->resolveReferences(R);
|
|
if (NewRec != Rec)
|
|
return FieldInit::get(NewRec, FieldName)->Fold(R.getCurrentRecord());
|
|
return this;
|
|
}
|
|
|
|
const Init *FieldInit::Fold(const Record *CurRec) const {
|
|
if (const auto *DI = dyn_cast<DefInit>(Rec)) {
|
|
const 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");
|
|
const Init *FieldVal = Def->getValue(FieldName)->getValue();
|
|
if (FieldVal->isConcrete())
|
|
return FieldVal;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
bool FieldInit::isConcrete() const {
|
|
if (const auto *DI = dyn_cast<DefInit>(Rec)) {
|
|
const Init *FieldVal = DI->getDef()->getValue(FieldName)->getValue();
|
|
return FieldVal->isConcrete();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void ProfileCondOpInit(FoldingSetNodeID &ID,
|
|
ArrayRef<const Init *> CondRange,
|
|
ArrayRef<const Init *> ValRange,
|
|
const RecTy *ValType) {
|
|
assert(CondRange.size() == ValRange.size() &&
|
|
"Number of conditions and values must match!");
|
|
ID.AddPointer(ValType);
|
|
ArrayRef<const Init *>::iterator Case = CondRange.begin();
|
|
ArrayRef<const Init *>::iterator Val = ValRange.begin();
|
|
|
|
while (Case != CondRange.end()) {
|
|
ID.AddPointer(*Case++);
|
|
ID.AddPointer(*Val++);
|
|
}
|
|
}
|
|
|
|
void CondOpInit::Profile(FoldingSetNodeID &ID) const {
|
|
ProfileCondOpInit(
|
|
ID, ArrayRef(getTrailingObjects<const Init *>(), NumConds),
|
|
ArrayRef(getTrailingObjects<const Init *>() + NumConds, NumConds),
|
|
ValType);
|
|
}
|
|
|
|
const CondOpInit *CondOpInit::get(ArrayRef<const Init *> CondRange,
|
|
ArrayRef<const Init *> ValRange,
|
|
const RecTy *Ty) {
|
|
assert(CondRange.size() == ValRange.size() &&
|
|
"Number of conditions and values must match!");
|
|
|
|
FoldingSetNodeID ID;
|
|
ProfileCondOpInit(ID, CondRange, ValRange, Ty);
|
|
|
|
detail::RecordKeeperImpl &RK = Ty->getRecordKeeper().getImpl();
|
|
void *IP = nullptr;
|
|
if (const CondOpInit *I = RK.TheCondOpInitPool.FindNodeOrInsertPos(ID, IP))
|
|
return I;
|
|
|
|
void *Mem = RK.Allocator.Allocate(
|
|
totalSizeToAlloc<const Init *>(2 * CondRange.size()), alignof(BitsInit));
|
|
CondOpInit *I = new(Mem) CondOpInit(CondRange.size(), Ty);
|
|
|
|
std::uninitialized_copy(CondRange.begin(), CondRange.end(),
|
|
I->getTrailingObjects<const Init *>());
|
|
std::uninitialized_copy(ValRange.begin(), ValRange.end(),
|
|
I->getTrailingObjects<const Init *>() +
|
|
CondRange.size());
|
|
RK.TheCondOpInitPool.InsertNode(I, IP);
|
|
return I;
|
|
}
|
|
|
|
const Init *CondOpInit::resolveReferences(Resolver &R) const {
|
|
SmallVector<const Init *, 4> NewConds;
|
|
bool Changed = false;
|
|
for (const Init *Case : getConds()) {
|
|
const Init *NewCase = Case->resolveReferences(R);
|
|
NewConds.push_back(NewCase);
|
|
Changed |= NewCase != Case;
|
|
}
|
|
|
|
SmallVector<const Init *, 4> NewVals;
|
|
for (const Init *Val : getVals()) {
|
|
const Init *NewVal = Val->resolveReferences(R);
|
|
NewVals.push_back(NewVal);
|
|
Changed |= NewVal != Val;
|
|
}
|
|
|
|
if (Changed)
|
|
return (CondOpInit::get(NewConds, NewVals,
|
|
getValType()))->Fold(R.getCurrentRecord());
|
|
|
|
return this;
|
|
}
|
|
|
|
const Init *CondOpInit::Fold(const Record *CurRec) const {
|
|
RecordKeeper &RK = getRecordKeeper();
|
|
for (unsigned i = 0; i < NumConds; ++i) {
|
|
const Init *Cond = getCond(i);
|
|
const Init *Val = getVal(i);
|
|
|
|
if (const auto *CondI = dyn_cast_or_null<IntInit>(
|
|
Cond->convertInitializerTo(IntRecTy::get(RK)))) {
|
|
if (CondI->getValue())
|
|
return Val->convertInitializerTo(getValType());
|
|
} else {
|
|
return this;
|
|
}
|
|
}
|
|
|
|
PrintFatalError(CurRec->getLoc(),
|
|
CurRec->getNameInitAsString() +
|
|
" 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 + ")";
|
|
}
|
|
|
|
const Init *CondOpInit::getBit(unsigned Bit) const {
|
|
return VarBitInit::get(this, Bit);
|
|
}
|
|
|
|
static void ProfileDagInit(FoldingSetNodeID &ID, const Init *V,
|
|
const StringInit *VN,
|
|
ArrayRef<const Init *> ArgRange,
|
|
ArrayRef<const StringInit *> NameRange) {
|
|
ID.AddPointer(V);
|
|
ID.AddPointer(VN);
|
|
|
|
ArrayRef<const Init *>::iterator Arg = ArgRange.begin();
|
|
ArrayRef<const StringInit *>::iterator Name = NameRange.begin();
|
|
while (Arg != ArgRange.end()) {
|
|
assert(Name != NameRange.end() && "Arg name underflow!");
|
|
ID.AddPointer(*Arg++);
|
|
ID.AddPointer(*Name++);
|
|
}
|
|
assert(Name == NameRange.end() && "Arg name overflow!");
|
|
}
|
|
|
|
const DagInit *DagInit::get(const Init *V, const StringInit *VN,
|
|
ArrayRef<const Init *> ArgRange,
|
|
ArrayRef<const StringInit *> NameRange) {
|
|
assert(ArgRange.size() == NameRange.size());
|
|
FoldingSetNodeID ID;
|
|
ProfileDagInit(ID, V, VN, ArgRange, NameRange);
|
|
|
|
detail::RecordKeeperImpl &RK = V->getRecordKeeper().getImpl();
|
|
void *IP = nullptr;
|
|
if (const DagInit *I = RK.TheDagInitPool.FindNodeOrInsertPos(ID, IP))
|
|
return I;
|
|
|
|
void *Mem =
|
|
RK.Allocator.Allocate(totalSizeToAlloc<const Init *, const 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<const Init *>());
|
|
std::uninitialized_copy(NameRange.begin(), NameRange.end(),
|
|
I->getTrailingObjects<const StringInit *>());
|
|
RK.TheDagInitPool.InsertNode(I, IP);
|
|
return I;
|
|
}
|
|
|
|
const DagInit *
|
|
DagInit::get(const Init *V, const StringInit *VN,
|
|
ArrayRef<std::pair<const Init *, const StringInit *>> args) {
|
|
SmallVector<const Init *, 8> Args;
|
|
SmallVector<const StringInit *, 8> Names;
|
|
|
|
for (const auto &Arg : args) {
|
|
Args.push_back(Arg.first);
|
|
Names.push_back(Arg.second);
|
|
}
|
|
|
|
return DagInit::get(V, VN, Args, Names);
|
|
}
|
|
|
|
void DagInit::Profile(FoldingSetNodeID &ID) const {
|
|
ProfileDagInit(
|
|
ID, Val, ValName, ArrayRef(getTrailingObjects<const Init *>(), NumArgs),
|
|
ArrayRef(getTrailingObjects<const StringInit *>(), NumArgNames));
|
|
}
|
|
|
|
const Record *DagInit::getOperatorAsDef(ArrayRef<SMLoc> Loc) const {
|
|
if (const auto *DefI = dyn_cast<DefInit>(Val))
|
|
return DefI->getDef();
|
|
PrintFatalError(Loc, "Expected record as operator");
|
|
return nullptr;
|
|
}
|
|
|
|
std::optional<unsigned> DagInit::getArgNo(StringRef Name) const {
|
|
for (unsigned i = 0, e = getNumArgs(); i < e; ++i) {
|
|
const StringInit *ArgName = getArgName(i);
|
|
if (ArgName && ArgName->getValue() == Name)
|
|
return i;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
const Init *DagInit::resolveReferences(Resolver &R) const {
|
|
SmallVector<const Init *, 8> NewArgs;
|
|
NewArgs.reserve(arg_size());
|
|
bool ArgsChanged = false;
|
|
for (const Init *Arg : getArgs()) {
|
|
const Init *NewArg = Arg->resolveReferences(R);
|
|
NewArgs.push_back(NewArg);
|
|
ArgsChanged |= NewArg != Arg;
|
|
}
|
|
|
|
const Init *Op = Val->resolveReferences(R);
|
|
if (Op != Val || ArgsChanged)
|
|
return DagInit::get(Op, ValName, NewArgs, getArgNames());
|
|
|
|
return this;
|
|
}
|
|
|
|
bool DagInit::isConcrete() const {
|
|
if (!Val->isConcrete())
|
|
return false;
|
|
for (const Init *Elt : getArgs()) {
|
|
if (!Elt->isConcrete())
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string DagInit::getAsString() const {
|
|
std::string Result = "(" + Val->getAsString();
|
|
if (ValName)
|
|
Result += ":" + ValName->getAsUnquotedString();
|
|
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();
|
|
}
|
|
}
|
|
return Result + ")";
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Other implementations
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
RecordVal::RecordVal(const Init *N, const RecTy *T, FieldKind K)
|
|
: Name(N), TyAndKind(T, K) {
|
|
setValue(UnsetInit::get(N->getRecordKeeper()));
|
|
assert(Value && "Cannot create unset value for current type!");
|
|
}
|
|
|
|
// This constructor accepts the same arguments as the above, but also
|
|
// a source location.
|
|
RecordVal::RecordVal(const Init *N, SMLoc Loc, const RecTy *T, FieldKind K)
|
|
: Name(N), Loc(Loc), TyAndKind(T, K) {
|
|
setValue(UnsetInit::get(N->getRecordKeeper()));
|
|
assert(Value && "Cannot create unset value for current type!");
|
|
}
|
|
|
|
StringRef RecordVal::getName() const {
|
|
return cast<StringInit>(getNameInit())->getValue();
|
|
}
|
|
|
|
std::string RecordVal::getPrintType() const {
|
|
if (getType() == StringRecTy::get(getRecordKeeper())) {
|
|
if (const auto *StrInit = dyn_cast<StringInit>(Value)) {
|
|
if (StrInit->hasCodeFormat())
|
|
return "code";
|
|
else
|
|
return "string";
|
|
} else {
|
|
return "string";
|
|
}
|
|
} else {
|
|
return TyAndKind.getPointer()->getAsString();
|
|
}
|
|
}
|
|
|
|
bool RecordVal::setValue(const Init *V) {
|
|
if (V) {
|
|
Value = V->getCastTo(getType());
|
|
if (Value) {
|
|
assert(!isa<TypedInit>(Value) ||
|
|
cast<TypedInit>(Value)->getType()->typeIsA(getType()));
|
|
if (const auto *BTy = dyn_cast<BitsRecTy>(getType())) {
|
|
if (!isa<BitsInit>(Value)) {
|
|
SmallVector<const Init *, 64> Bits;
|
|
Bits.reserve(BTy->getNumBits());
|
|
for (unsigned I = 0, E = BTy->getNumBits(); I < E; ++I)
|
|
Bits.push_back(Value->getBit(I));
|
|
Value = BitsInit::get(V->getRecordKeeper(), Bits);
|
|
}
|
|
}
|
|
}
|
|
return Value == nullptr;
|
|
}
|
|
Value = nullptr;
|
|
return false;
|
|
}
|
|
|
|
// This version of setValue takes a source location and resets the
|
|
// location in the RecordVal.
|
|
bool RecordVal::setValue(const 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 (const auto *BTy = dyn_cast<BitsRecTy>(getType())) {
|
|
if (!isa<BitsInit>(Value)) {
|
|
SmallVector<const Init *, 64> Bits;
|
|
Bits.reserve(BTy->getNumBits());
|
|
for (unsigned I = 0, E = BTy->getNumBits(); I < E; ++I)
|
|
Bits.push_back(Value->getBit(I));
|
|
Value = BitsInit::get(getRecordKeeper(), Bits);
|
|
}
|
|
}
|
|
}
|
|
return Value == nullptr;
|
|
}
|
|
Value = nullptr;
|
|
return false;
|
|
}
|
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
#include "llvm/TableGen/Record.h"
|
|
LLVM_DUMP_METHOD void RecordVal::dump() const { errs() << *this; }
|
|
#endif
|
|
|
|
void RecordVal::print(raw_ostream &OS, bool PrintSem) const {
|
|
if (isNonconcreteOK()) OS << "field ";
|
|
OS << getPrintType() << " " << getNameInitAsString();
|
|
|
|
if (getValue())
|
|
OS << " = " << *getValue();
|
|
|
|
if (PrintSem) OS << ";\n";
|
|
}
|
|
|
|
void Record::updateClassLoc(SMLoc Loc) {
|
|
assert(Locs.size() == 1);
|
|
ForwardDeclarationLocs.push_back(Locs.front());
|
|
|
|
Locs.clear();
|
|
Locs.push_back(Loc);
|
|
}
|
|
|
|
void Record::checkName() {
|
|
// Ensure the record name has string type.
|
|
const auto *TypedName = cast<const TypedInit>(Name);
|
|
if (!isa<StringRecTy>(TypedName->getType()))
|
|
PrintFatalError(getLoc(), Twine("Record name '") + Name->getAsString() +
|
|
"' is not a string!");
|
|
}
|
|
|
|
const RecordRecTy *Record::getType() const {
|
|
SmallVector<const Record *, 4> DirectSCs;
|
|
getDirectSuperClasses(DirectSCs);
|
|
return RecordRecTy::get(TrackedRecords, DirectSCs);
|
|
}
|
|
|
|
DefInit *Record::getDefInit() const {
|
|
if (!CorrespondingDefInit) {
|
|
CorrespondingDefInit =
|
|
new (TrackedRecords.getImpl().Allocator) DefInit(this);
|
|
}
|
|
return CorrespondingDefInit;
|
|
}
|
|
|
|
unsigned Record::getNewUID(RecordKeeper &RK) {
|
|
return RK.getImpl().LastRecordID++;
|
|
}
|
|
|
|
void Record::setName(const Init *NewName) {
|
|
Name = NewName;
|
|
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.
|
|
}
|
|
|
|
// 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<const 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;
|
|
}
|
|
|
|
void Record::getDirectSuperClasses(
|
|
SmallVectorImpl<const Record *> &Classes) const {
|
|
ArrayRef<std::pair<const Record *, SMRange>> SCs = getSuperClasses();
|
|
|
|
while (!SCs.empty()) {
|
|
const Record *SC = SCs.back().first;
|
|
SCs = SCs.drop_back(1 + SC->getSuperClasses().size());
|
|
Classes.push_back(SC);
|
|
}
|
|
}
|
|
|
|
void Record::resolveReferences(Resolver &R, const RecordVal *SkipVal) {
|
|
const Init *OldName = getNameInit();
|
|
const Init *NewName = Name->resolveReferences(R);
|
|
if (NewName != OldName) {
|
|
// Re-register with RecordKeeper.
|
|
setName(NewName);
|
|
}
|
|
|
|
// Resolve the field values.
|
|
for (RecordVal &Value : Values) {
|
|
if (SkipVal == &Value) // Skip resolve the same field as the given one
|
|
continue;
|
|
if (const Init *V = Value.getValue()) {
|
|
const Init *VR = V->resolveReferences(R);
|
|
if (Value.setValue(VR)) {
|
|
std::string Type;
|
|
if (const auto *VRT = dyn_cast<TypedInit>(VR))
|
|
Type =
|
|
(Twine("of type '") + VRT->getType()->getAsString() + "' ").str();
|
|
PrintFatalError(
|
|
getLoc(),
|
|
Twine("Invalid value ") + Type + "found when setting field '" +
|
|
Value.getNameInitAsString() + "' of type '" +
|
|
Value.getType()->getAsString() +
|
|
"' after resolving references: " + VR->getAsUnquotedString() +
|
|
"\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Resolve the assertion expressions.
|
|
for (auto &Assertion : Assertions) {
|
|
const Init *Value = Assertion.Condition->resolveReferences(R);
|
|
Assertion.Condition = Value;
|
|
Value = Assertion.Message->resolveReferences(R);
|
|
Assertion.Message = Value;
|
|
}
|
|
// Resolve the dump expressions.
|
|
for (auto &Dump : Dumps) {
|
|
const Init *Value = Dump.Message->resolveReferences(R);
|
|
Dump.Message = Value;
|
|
}
|
|
}
|
|
|
|
void Record::resolveReferences(const Init *NewName) {
|
|
RecordResolver R(*this);
|
|
R.setName(NewName);
|
|
R.setFinal(true);
|
|
resolveReferences(R);
|
|
}
|
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
LLVM_DUMP_METHOD void Record::dump() const { errs() << *this; }
|
|
#endif
|
|
|
|
raw_ostream &llvm::operator<<(raw_ostream &OS, const Record &R) {
|
|
OS << R.getNameInitAsString();
|
|
|
|
ArrayRef<const Init *> TArgs = R.getTemplateArgs();
|
|
if (!TArgs.empty()) {
|
|
OS << "<";
|
|
bool NeedComma = false;
|
|
for (const Init *TA : TArgs) {
|
|
if (NeedComma) OS << ", ";
|
|
NeedComma = true;
|
|
const RecordVal *RV = R.getValue(TA);
|
|
assert(RV && "Template argument record not found??");
|
|
RV->print(OS, false);
|
|
}
|
|
OS << ">";
|
|
}
|
|
|
|
OS << " {";
|
|
ArrayRef<std::pair<const Record *, SMRange>> SC = R.getSuperClasses();
|
|
if (!SC.empty()) {
|
|
OS << "\t//";
|
|
for (const auto &[SC, _] : SC)
|
|
OS << " " << SC->getNameInitAsString();
|
|
}
|
|
OS << "\n";
|
|
|
|
for (const RecordVal &Val : R.getValues())
|
|
if (Val.isNonconcreteOK() && !R.isTemplateArg(Val.getNameInit()))
|
|
OS << Val;
|
|
for (const RecordVal &Val : R.getValues())
|
|
if (!Val.isNonconcreteOK() && !R.isTemplateArg(Val.getNameInit()))
|
|
OS << Val;
|
|
|
|
return OS << "}\n";
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
const Init *Record::getValueInit(StringRef FieldName) const {
|
|
const RecordVal *R = getValue(FieldName);
|
|
if (!R || !R->getValue())
|
|
PrintFatalError(getLoc(), "Record `" + getName() +
|
|
"' does not have a field named `" + FieldName + "'!\n");
|
|
return R->getValue();
|
|
}
|
|
|
|
StringRef Record::getValueAsString(StringRef FieldName) const {
|
|
std::optional<StringRef> S = getValueAsOptionalString(FieldName);
|
|
if (!S)
|
|
PrintFatalError(getLoc(), "Record `" + getName() +
|
|
"' does not have a field named `" + FieldName + "'!\n");
|
|
return *S;
|
|
}
|
|
|
|
std::optional<StringRef>
|
|
Record::getValueAsOptionalString(StringRef FieldName) const {
|
|
const RecordVal *R = getValue(FieldName);
|
|
if (!R || !R->getValue())
|
|
return std::nullopt;
|
|
if (isa<UnsetInit>(R->getValue()))
|
|
return std::nullopt;
|
|
|
|
if (const auto *SI = dyn_cast<StringInit>(R->getValue()))
|
|
return SI->getValue();
|
|
|
|
PrintFatalError(getLoc(),
|
|
"Record `" + getName() + "', ` field `" + FieldName +
|
|
"' exists but does not have a string initializer!");
|
|
}
|
|
|
|
const BitsInit *Record::getValueAsBitsInit(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 (const auto *BI = dyn_cast<BitsInit>(R->getValue()))
|
|
return BI;
|
|
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + FieldName +
|
|
"' exists but does not have a bits value");
|
|
}
|
|
|
|
const ListInit *Record::getValueAsListInit(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 (const auto *LI = dyn_cast<ListInit>(R->getValue()))
|
|
return LI;
|
|
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + FieldName +
|
|
"' exists but does not have a list value");
|
|
}
|
|
|
|
std::vector<const Record *>
|
|
Record::getValueAsListOfDefs(StringRef FieldName) const {
|
|
const ListInit *List = getValueAsListInit(FieldName);
|
|
std::vector<const Record *> Defs;
|
|
for (const Init *I : List->getValues()) {
|
|
if (const auto *DI = dyn_cast<DefInit>(I))
|
|
Defs.push_back(DI->getDef());
|
|
else
|
|
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" +
|
|
FieldName +
|
|
"' list is not entirely DefInit!");
|
|
}
|
|
return Defs;
|
|
}
|
|
|
|
int64_t Record::getValueAsInt(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 (const auto *II = dyn_cast<IntInit>(R->getValue()))
|
|
return II->getValue();
|
|
PrintFatalError(getLoc(), Twine("Record `") + getName() + "', field `" +
|
|
FieldName +
|
|
"' exists but does not have an int value: " +
|
|
R->getValue()->getAsString());
|
|
}
|
|
|
|
std::vector<int64_t>
|
|
Record::getValueAsListOfInts(StringRef FieldName) const {
|
|
const ListInit *List = getValueAsListInit(FieldName);
|
|
std::vector<int64_t> Ints;
|
|
for (const Init *I : List->getValues()) {
|
|
if (const auto *II = dyn_cast<IntInit>(I))
|
|
Ints.push_back(II->getValue());
|
|
else
|
|
PrintFatalError(getLoc(),
|
|
Twine("Record `") + getName() + "', field `" + FieldName +
|
|
"' exists but does not have a list of ints value: " +
|
|
I->getAsString());
|
|
}
|
|
return Ints;
|
|
}
|
|
|
|
std::vector<StringRef>
|
|
Record::getValueAsListOfStrings(StringRef FieldName) const {
|
|
const ListInit *List = getValueAsListInit(FieldName);
|
|
std::vector<StringRef> Strings;
|
|
for (const Init *I : List->getValues()) {
|
|
if (const auto *SI = dyn_cast<StringInit>(I))
|
|
Strings.push_back(SI->getValue());
|
|
else
|
|
PrintFatalError(getLoc(),
|
|
Twine("Record `") + getName() + "', field `" + FieldName +
|
|
"' exists but does not have a list of strings value: " +
|
|
I->getAsString());
|
|
}
|
|
return Strings;
|
|
}
|
|
|
|
const Record *Record::getValueAsDef(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 (const auto *DI = dyn_cast<DefInit>(R->getValue()))
|
|
return DI->getDef();
|
|
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" +
|
|
FieldName + "' does not have a def initializer!");
|
|
}
|
|
|
|
const 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 (const auto *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 '?'!");
|
|
}
|
|
|
|
bool Record::getValueAsBit(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 (const auto *BI = dyn_cast<BitInit>(R->getValue()))
|
|
return BI->getValue();
|
|
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" +
|
|
FieldName + "' does not have a bit initializer!");
|
|
}
|
|
|
|
bool Record::getValueAsBitOrUnset(StringRef FieldName, bool &Unset) const {
|
|
const RecordVal *R = getValue(FieldName);
|
|
if (!R || !R->getValue())
|
|
PrintFatalError(getLoc(), "Record `" + getName() +
|
|
"' does not have a field named `" + FieldName.str() + "'!\n");
|
|
|
|
if (isa<UnsetInit>(R->getValue())) {
|
|
Unset = true;
|
|
return false;
|
|
}
|
|
Unset = false;
|
|
if (const auto *BI = dyn_cast<BitInit>(R->getValue()))
|
|
return BI->getValue();
|
|
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" +
|
|
FieldName + "' does not have a bit initializer!");
|
|
}
|
|
|
|
const DagInit *Record::getValueAsDag(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 (const auto *DI = dyn_cast<DagInit>(R->getValue()))
|
|
return DI;
|
|
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" +
|
|
FieldName + "' does not have a dag initializer!");
|
|
}
|
|
|
|
// 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.
|
|
void Record::checkRecordAssertions() {
|
|
RecordResolver R(*this);
|
|
R.setFinal(true);
|
|
|
|
bool AnyFailed = false;
|
|
for (const auto &Assertion : getAssertions()) {
|
|
const Init *Condition = Assertion.Condition->resolveReferences(R);
|
|
const Init *Message = Assertion.Message->resolveReferences(R);
|
|
AnyFailed |= CheckAssert(Assertion.Loc, Condition, Message);
|
|
}
|
|
|
|
if (!AnyFailed)
|
|
return;
|
|
|
|
// If any of the record assertions failed, print some context that will
|
|
// help see where the record that caused these assert failures is defined.
|
|
PrintError(this, "assertion failed in this record");
|
|
}
|
|
|
|
void Record::emitRecordDumps() {
|
|
RecordResolver R(*this);
|
|
R.setFinal(true);
|
|
|
|
for (const auto &Dump : getDumps()) {
|
|
const Init *Message = Dump.Message->resolveReferences(R);
|
|
dumpMessage(Dump.Loc, Message);
|
|
}
|
|
}
|
|
|
|
// Report a warning if the record has unused template arguments.
|
|
void Record::checkUnusedTemplateArgs() {
|
|
for (const Init *TA : getTemplateArgs()) {
|
|
const RecordVal *Arg = getValue(TA);
|
|
if (!Arg->isUsed())
|
|
PrintWarning(Arg->getLoc(),
|
|
"unused template argument: " + Twine(Arg->getName()));
|
|
}
|
|
}
|
|
|
|
RecordKeeper::RecordKeeper()
|
|
: Impl(std::make_unique<detail::RecordKeeperImpl>(*this)),
|
|
Timer(std::make_unique<TGTimer>()) {}
|
|
|
|
RecordKeeper::~RecordKeeper() = default;
|
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
LLVM_DUMP_METHOD void RecordKeeper::dump() const { errs() << *this; }
|
|
#endif
|
|
|
|
raw_ostream &llvm::operator<<(raw_ostream &OS, const RecordKeeper &RK) {
|
|
OS << "------------- Classes -----------------\n";
|
|
for (const auto &C : RK.getClasses())
|
|
OS << "class " << *C.second;
|
|
|
|
OS << "------------- Defs -----------------\n";
|
|
for (const auto &D : RK.getDefs())
|
|
OS << "def " << *D.second;
|
|
return OS;
|
|
}
|
|
|
|
/// GetNewAnonymousName - Generate a unique anonymous name that can be used as
|
|
/// an identifier.
|
|
const Init *RecordKeeper::getNewAnonymousName() {
|
|
return AnonymousNameInit::get(*this, getImpl().AnonCounter++);
|
|
}
|
|
|
|
ArrayRef<const Record *>
|
|
RecordKeeper::getAllDerivedDefinitions(StringRef ClassName) const {
|
|
// We cache the record vectors for single classes. Many backends request
|
|
// the same vectors multiple times.
|
|
auto [Iter, Inserted] = Cache.try_emplace(ClassName.str());
|
|
if (Inserted)
|
|
Iter->second = getAllDerivedDefinitions(ArrayRef(ClassName));
|
|
return Iter->second;
|
|
}
|
|
|
|
std::vector<const Record *>
|
|
RecordKeeper::getAllDerivedDefinitions(ArrayRef<StringRef> ClassNames) const {
|
|
SmallVector<const Record *, 2> ClassRecs;
|
|
std::vector<const Record *> Defs;
|
|
|
|
assert(ClassNames.size() > 0 && "At least one class must be passed.");
|
|
for (const auto &ClassName : ClassNames) {
|
|
const 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());
|
|
}
|
|
llvm::sort(Defs, LessRecord());
|
|
return Defs;
|
|
}
|
|
|
|
ArrayRef<const Record *>
|
|
RecordKeeper::getAllDerivedDefinitionsIfDefined(StringRef ClassName) const {
|
|
if (getClass(ClassName))
|
|
return getAllDerivedDefinitions(ClassName);
|
|
return Cache[""];
|
|
}
|
|
|
|
void RecordKeeper::dumpAllocationStats(raw_ostream &OS) const {
|
|
Impl->dumpAllocationStats(OS);
|
|
}
|
|
|
|
const Init *MapResolver::resolve(const Init *VarName) {
|
|
auto It = Map.find(VarName);
|
|
if (It == Map.end())
|
|
return nullptr;
|
|
|
|
const 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;
|
|
}
|
|
|
|
const Init *RecordResolver::resolve(const Init *VarName) {
|
|
const Init *Val = Cache.lookup(VarName);
|
|
if (Val)
|
|
return Val;
|
|
|
|
if (llvm::is_contained(Stack, VarName))
|
|
return nullptr; // prevent infinite recursion
|
|
|
|
if (const RecordVal *RV = getCurrentRecord()->getValue(VarName)) {
|
|
if (!isa<UnsetInit>(RV->getValue())) {
|
|
Val = RV->getValue();
|
|
Stack.push_back(VarName);
|
|
Val = Val->resolveReferences(*this);
|
|
Stack.pop_back();
|
|
}
|
|
} else if (Name && VarName == getCurrentRecord()->getNameInit()) {
|
|
Stack.push_back(VarName);
|
|
Val = Name->resolveReferences(*this);
|
|
Stack.pop_back();
|
|
}
|
|
|
|
Cache[VarName] = Val;
|
|
return Val;
|
|
}
|
|
|
|
const Init *TrackUnresolvedResolver::resolve(const Init *VarName) {
|
|
const 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;
|
|
}
|
|
|
|
const Init *HasReferenceResolver::resolve(const Init *VarName) {
|
|
if (VarName == VarNameToTrack)
|
|
Found = true;
|
|
return nullptr;
|
|
}
|