mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
52b617a6dd
These are the elementary steps of the DataLayoutAnalysis (that is still to be defined), along with a manager to handle them, their dependencies, their executions and their invalidations. In principle, this design could have been implemented inheriting from `llvm::Pass` and `llvm::legacy::PassManager` (or `llvm::PassInfoMixin` and `llvm::PMDataManager` for the new step manager). However, LLVM's passes and managers provide far more complex features than we need for now, so I decided to keep it simple and roll our own for now. We could potentially switch to LLVM stuff later if we need their power. Some examples of features we don't need for now are: - capability to pass results from a pass to another that depends on it - capability to dynamically compute pass dependencies - capability to dynamically invalidate passes If some of these become necessary in future, it's a sign that it might be time to switch to an LLVM-based design The design is structured as follows. 1) Adding `Step`s to the `StepManager` The `StepManager` owns the `Step`s, that can be added via the `addStep` method. Whenever a new `Step` is added, four properties are checked: - that all its dependencies have already been added before it; - that non of its dependencies have been invalidated by the insertion of another step that invalidate them. - that it does not depend on itself - that it does not invalidate itself If any of these conditions fail, the `Step` is not added successfully. Whenever a new `Step` A is added and it invalidates any `Step` B, the last instance of B added to the `StepManager` before A (if present) is marked as invalidated. If one wants to add a new `Step` C that depends on B, it will be necessary to explicitly add a new instance of A (say A*) before adding C. Otherwise, there will be no guarantee that an instance of A executes before C without being invalidated. These conditions on invalidations are restrictive but the allow to specify the dependencies and invalidations that we need for now. If it turns out we need more complex dependencies and invalidations we should explore implementing the LLVM-based design. 2) Executing the added `Step`s After adding the `Step`s to the `StepManger`, they can be executed all together with the `dla::StepManger::run()` method. This method runs all the steps in the order they were added by the user with calls to `addStep`. The fact that `addStep` strictly ensures that dependencies and invalidations are respected, guarantees that the execution always works in the order of `Step`s specified by the user. It also means that there is no need to check and propagate invalidations across dependencies between the execution of the `Step`s, because there is the guarantee that if a `Step` A invalidates `Step` B, another instance of B will be executed later if some `Step` C needs it, or if the user has added it explicitly. When the `dla::StepManager::run()` method returns, all the `Step`s that were successfully added to the manager have finished running. 3) Implementing a `Step` Each `Step` has a virtual `runOnTypeSystem` method, that executes the `Step` and returns true if some change was made, false otherwise. `TypeSystem` for now is just a forward declaration and all the implementations of `runOnTypeSystem` simply return true.
235 lines
6.4 KiB
C++
235 lines
6.4 KiB
C++
/// \file DLAStepManager.cpp
|
|
/// \brief Tests for dla::StepManager
|
|
|
|
//
|
|
// Copyright (c) rev.ng Srls. See LICENSE.md for details.
|
|
//
|
|
|
|
#define BOOST_TEST_MODULE DLAStepManager
|
|
bool init_unit_test();
|
|
#include "boost/test/unit_test.hpp"
|
|
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
|
|
|
#include "revng/Support/Assert.h"
|
|
#include "revng/UnitTestHelpers/UnitTestHelpers.h"
|
|
|
|
#include "lib/Decompiler/DLAStep.h"
|
|
|
|
namespace dla {
|
|
|
|
class SelfDependentStep : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
|
|
SelfDependentStep() : Step(ID, { SelfDependentStep::getID() }, {}) {}
|
|
|
|
virtual ~SelfDependentStep() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override { return false; }
|
|
};
|
|
|
|
const char SelfDependentStep::ID = 0;
|
|
|
|
class SelfInvalidatingStep : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
|
|
SelfInvalidatingStep() : Step(ID, {}, { SelfInvalidatingStep::getID() }) {}
|
|
|
|
virtual ~SelfInvalidatingStep() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override { return false; }
|
|
};
|
|
|
|
const char SelfInvalidatingStep::ID = 0;
|
|
|
|
class StepWithNoDeps : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
|
|
StepWithNoDeps() : Step(ID) {}
|
|
|
|
virtual ~StepWithNoDeps() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override { return false; }
|
|
};
|
|
|
|
const char StepWithNoDeps::ID = 0;
|
|
|
|
class SingleDependencyStep : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
|
|
SingleDependencyStep() : Step(ID, { StepWithNoDeps::getID() }, {}) {}
|
|
|
|
virtual ~SingleDependencyStep() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override { return false; }
|
|
};
|
|
|
|
const char SingleDependencyStep::ID = 0;
|
|
|
|
class StepInvalidateNoDeps : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
|
|
StepInvalidateNoDeps() : Step(ID, {}, { StepWithNoDeps::getID() }) {}
|
|
|
|
virtual ~StepInvalidateNoDeps() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override { return false; }
|
|
};
|
|
|
|
const char StepInvalidateNoDeps::ID = 0;
|
|
|
|
} // end namespace dla
|
|
|
|
using namespace dla;
|
|
|
|
BOOST_AUTO_TEST_CASE(InitiallyEmpty) {
|
|
StepManager SM;
|
|
|
|
BOOST_TEST(SM.getNumSteps() == 0);
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
|
|
SM.reset();
|
|
|
|
BOOST_TEST(SM.getNumSteps() == 0);
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(InsertSelfDependency) {
|
|
StepManager SM;
|
|
|
|
BOOST_TEST(not SM.addStep(std::unique_ptr<Step>(new SelfDependentStep())));
|
|
BOOST_TEST(SM.getNumSteps() == 0);
|
|
// The schedule is still valid because the bad step is not inserted
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(InsertSelfInvalidation) {
|
|
StepManager SM;
|
|
|
|
BOOST_TEST(not SM.addStep(std::unique_ptr<Step>(new SelfInvalidatingStep())));
|
|
BOOST_TEST(SM.getNumSteps() == 0);
|
|
// The schedule is still valid because the bad step is not inserted
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(InsertStepWithNoDeps) {
|
|
StepManager SM;
|
|
|
|
BOOST_TEST(SM.addStep(std::unique_ptr<Step>(new StepWithNoDeps())));
|
|
|
|
BOOST_TEST(SM.getNumSteps() == 1);
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
|
|
SM.reset();
|
|
|
|
BOOST_TEST(SM.getNumSteps() == 0);
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(InsertSameStepTwice) {
|
|
StepManager SM;
|
|
BOOST_TEST(SM.addStep(std::make_unique<StepWithNoDeps>()));
|
|
|
|
BOOST_TEST(SM.getNumSteps() == 1);
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
|
|
BOOST_TEST(SM.addStep(std::make_unique<StepWithNoDeps>()));
|
|
|
|
BOOST_TEST(SM.getNumSteps() == 2);
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
|
|
SM.reset();
|
|
|
|
BOOST_TEST(SM.getNumSteps() == 0);
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(InsertWithValidDependency) {
|
|
StepManager SM;
|
|
|
|
BOOST_TEST(SM.addStep(std::make_unique<StepWithNoDeps>()));
|
|
|
|
BOOST_TEST(SM.getNumSteps() == 1);
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
|
|
BOOST_TEST(SM.addStep(std::make_unique<SingleDependencyStep>()));
|
|
|
|
BOOST_TEST(SM.getNumSteps() == 2);
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
|
|
SM.reset();
|
|
|
|
BOOST_TEST(SM.getNumSteps() == 0);
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(InsertWithMissingDependency) {
|
|
StepManager SM;
|
|
|
|
BOOST_TEST(not SM.addStep(std::make_unique<SingleDependencyStep>()));
|
|
BOOST_TEST(SM.getNumSteps() == 0);
|
|
// This must be true becase the step is not inserted
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(SimpleInvalidate) {
|
|
StepManager SM;
|
|
// Initially the sched is empty
|
|
BOOST_TEST(SM.sched_begin() == SM.sched_end());
|
|
|
|
// Inserting a step with no dependencies should always succeed
|
|
BOOST_TEST(SM.addStep(std::unique_ptr<Step>(new StepWithNoDeps())));
|
|
BOOST_TEST(SM.getNumSteps() == 1);
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
|
|
// Inserting a step with a valid dependency that is already scheduled should
|
|
// succeed and leave SM with a valid schedule
|
|
BOOST_TEST(SM.addStep(std::unique_ptr<Step>(new SingleDependencyStep())));
|
|
BOOST_TEST(SM.getNumSteps() == 2);
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
|
|
// Insert a step with no dependencies and that invalidates StepWithNoDeps.
|
|
// This should succeed, while invalidating StepWithNoDeps.
|
|
// If later no other step is added that depends on StepWithNoDeps, the
|
|
// schedule should still be valid.
|
|
BOOST_TEST(SM.addStep(std::unique_ptr<Step>(new StepInvalidateNoDeps())));
|
|
BOOST_TEST(SM.getNumSteps() == 3);
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
|
|
// If we now add a step that depends on the invalidated step StepWithNoDeps,
|
|
// the insertion should fail.
|
|
BOOST_TEST(not SM.addStep(std::unique_ptr<Step>(new SingleDependencyStep())));
|
|
BOOST_TEST(SM.getNumSteps() == 3);
|
|
// Again the schedule remains in a valid state, because the bad step is not
|
|
// added.
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
|
|
// We then add a new istance of the invalidated StepWithNoDeps, that is
|
|
// succesfully appended to the end of the schedule.
|
|
BOOST_TEST(SM.addStep(std::unique_ptr<Step>(new StepWithNoDeps())));
|
|
BOOST_TEST(SM.getNumSteps() == 4);
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
|
|
// Now the second instance of SingleDependencyStep can be added
|
|
// successfully because the second instance of StepWithNoDeps is scheduled
|
|
// before it and no other step invalidates it.
|
|
BOOST_TEST(SM.addStep(std::unique_ptr<Step>(new SingleDependencyStep())));
|
|
BOOST_TEST(SM.getNumSteps() == 5);
|
|
BOOST_TEST(SM.hasValidSchedule());
|
|
}
|