Files
revng-revng/include/revng/Pipeline/ContainerEnumerator.h
Giacomo Vercesi 11f1e532c6 Implement dirty bit on Container
Add a `Dirty` boolean to the `ContainerBase` class. This will be set to
true when the container mutates itself (e.g. remove, mergeBack, clear).
The dirty bit is reset when running `PipelineManager.store`.
2025-04-03 15:59:18 +02:00

151 lines
5.0 KiB
C++

#pragma once
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ManagedStatic.h"
#include "revng/Pipeline/ContainerSet.h"
#include "revng/Pipeline/Context.h"
#include "revng/Pipeline/Target.h"
namespace pipeline {
/// We sometime wish to allow a downstream library to extend what Kinds can be
/// contained in a container. An example is a LLVMContainer, it contains a
/// module which is composed of global objects, a downstream library may wish to
/// add a particular meaning of a global object. If that is the case then
/// ContainerEnumerator and EnumerableContainers are what you need.
///
/// If a downstream user of revng wishes to extend the enumeration and removal
/// rule from a container it can do so by extending this class, specialized with
/// the type of the container in question.
///
/// The enumerate and removeTarget methods must be implemented with the
/// appropriate rules, that is, enumerate must return Targets with the same kind
/// as the one pointed by K.
template<typename Container>
class ContainerEnumerator {
private:
Kind *K = nullptr;
public:
ContainerEnumerator(Kind &K) : K(&K) {
Container::getRegisteredInspectors().push_back(this);
}
public:
Kind &getKind() const { return *K; }
public:
virtual ~ContainerEnumerator() = default;
virtual TargetsList enumerate(const Container &ToInspect) const = 0;
/// \return must return true if it was possible to remove the provided target
virtual bool remove(const TargetsList &Targets,
Container &ToInspect) const = 0;
};
/// An inspectable container is a container that delegates the enumeration and
/// removal of its contents to inspectors, which can be dynamically registered
/// at runtime.
///
/// This facility is needed to be able to decouple the Kind declaration from the
/// containers declaration, since if it was always containers to name which
/// kinds can be contained in them, then they would not be able to name kinds
/// declared downstream libraries. Similarly, should be always kind to be able
/// to enumerate the context of a container, then any containers declared in
/// downstream libraries would not be able to use kinds declared upstream.
///
/// You should declare a new inspectable container if and only if you think that
/// downstream user may wish to extend how the enumeration of this container
/// happens.
/// Otherwise simply declare a new Container Type, the difference is that a
/// normal container must provide a enumeration method of its own rather than
/// leaving it up to inspectors.
template<typename Derived>
class EnumerableContainer : public Container<Derived> {
friend class ContainerEnumerator<Derived>;
private:
using StaticContainer = llvm::SmallVector<ContainerEnumerator<Derived> *, 4>;
static StaticContainer &getRegisteredInspectors() {
static StaticContainer List;
return List;
}
protected:
Context *TheContext = nullptr;
public:
EnumerableContainer(Context &Context, llvm::StringRef Name) :
Container<Derived>(Name), TheContext(&Context) {}
EnumerableContainer(Context &Context, llvm::StringRef Name, const char *ID) :
Container<Derived>(Name, ID), TheContext(&Context) {}
public:
const Context &getContext() const { return *TheContext; }
Context &getContext() { return *TheContext; }
bool contains(const Target &Target) const {
return enumerate().contains(Target);
}
public:
~EnumerableContainer() override = default;
TargetsList enumerate() const override {
TargetsList ToReturn;
for (const auto *Inspector : getRegisteredInspectors())
ToReturn.merge(Inspector->enumerate(*this->self()));
return ToReturn;
}
/// \return true if all targets to remove have been removed
bool removeImpl(const TargetsList &Targets) override {
bool RemovedAll = true;
for (const auto *Inspector : getRegisteredInspectors()) {
RemovedAll = Inspector->remove(Targets.filter(Inspector->getKind()),
*this->self())
and RemovedAll;
}
return RemovedAll;
}
static std::vector<Kind *> possibleKinds() {
std::vector<Kind *> ToReturn;
for (auto *Inspector : getRegisteredInspectors()) {
ToReturn.push_back(&Inspector->getKind());
}
return ToReturn;
}
};
template<typename Container>
class KindForContainer : public Kind, public ContainerEnumerator<Container> {
public:
template<RankSpecialization BaseRank>
KindForContainer(llvm::StringRef Name, const BaseRank &Rank) :
Kind(Name, Rank, {}, {}),
ContainerEnumerator<Container>(*static_cast<Kind *>(this)) {}
template<RankSpecialization BaseRank>
KindForContainer(llvm::StringRef Name, Kind &Parent, const BaseRank &Rank) :
Kind(Name, Parent, Rank, {}, {}),
ContainerEnumerator<Container>(*static_cast<Kind *>(this)) {}
public:
~KindForContainer() override = default;
};
} // namespace pipeline