20using Phases = std::deque<std::unique_ptr<Phase>>;
25class Phase :
public fe::RuntimeCast<Phase> {
36 virtual std::unique_ptr<Phase>
recreate();
53 world.
log().d(
"apply phase {}", p_def);
55 if (
auto axm = p_def->isa<
Axm>())
56 if (
auto i = phases.find(axm->flags()); i != phases.end()) {
57 auto phase = i->second(
world);
59 phase->apply(def->isa<
App>());
60 if (phase->redirects())
return phase->take_resolved();
64 fe::throwf(
"phase `{}` not found", axm->sym());
66 fe::throwf(
"unsupported callee for a phase: `{}`", p_def);
69 template<
class A,
class P>
79 const fe::Log&
log()
const {
return world_.log(); }
85 const fe::Vector<std::string>&
args();
90 bool todo()
const {
return todo_; }
107 template<
class P,
class... Args>
109 P p(std::forward<Args>(
args)...);
167 virtual void reset();
190 const auto&
lattice()
const {
return lattice_; }
194 if (
auto i = lattice_.find(def); i != lattice_.end())
return i->second;
207 auto a = follow(def);
210 return b ? repr_(a, b) : a;
215 auto i = lattice_.find(def);
216 return i != lattice_.end() && i->second == def;
233 if (
auto [i, ins] = lattice_.emplace(concr, abstr); !ins) {
234 if (i->second == abstr)
return false;
237 return touch(),
true;
249 if (
auto [i, ins] = lattice_.emplace(concr, abstr); !ins) {
250 assert((i->second != concr || abstr == concr) &&
"monotonicity violation: must not descend from ⊤");
251 if (i->second == abstr)
return false;
253 }
else if (concr == abstr) {
256 return touch(),
true;
271 void start()
override;
297 const Def* follow(
const Def* def)
const {
298 auto i = lattice_.find(def);
299 return i == lattice_.end() || i->second == def ? nullptr : i->second;
304 const Def* repr_(
const Def* slow,
const Def* fast)
const;
322 std::deque<Def*> worklist_;
325 bool nonlocal_ =
false;
326 bool curr_sparse_ =
false;
328 bool bootstrapping_ =
true;
329 size_t num_drained_ = 0;
379 const Def*
lattice(
const Def* def)
const {
return analysis_ ? analysis_->lattice(def) :
nullptr; }
384 return l && l != def ? l :
nullptr;
407 void start()
override;
419 bool bootstrapping_ =
true;
456 void start()
override;
537 if (
auto subst =
replace(def))
todo =
true, def = subst;
544#define MIM_CONCAT_INNER(a, b) a##b
545#define MIM_CONCAT(a, b) MIM_CONCAT_INNER(a, b)
547#define MIM_REPL(__phases, __annex, ...) MIM_REPL_IMPL(__phases, __annex, __LINE__, __VA_ARGS__)
550#define MIM_REPL_IMPL(__phases, __annex, __id, ...) \
551 struct MIM_CONCAT(Repl_, __id) : ::mim::Repl { \
552 MIM_CONCAT(Repl_, __id)(::mim::World & world, ::mim::flags_t annex) \
553 : Repl(world, annex) {} \
555 const ::mim::Def* replace(const ::mim::Def* def) final __VA_ARGS__ \
557 ::mim::Phase::hook<__annex, MIM_CONCAT(Repl_, __id)>(__phases)
589 const auto&
phases()
const {
return phases_; }
604template<class M =
Def>
624 M*
root()
const {
return root_; }
627 const bool elide_empty_;
628 const bool schedule_;
634template<
class M = Def>
Traverses the current World using Rewriter infrastructure while staying in the same world.
virtual void prepare()
Run before the main analysis.
size_t version() const
Bumped on every observable lattice change; snapshot it around a code region to detect changes.
void start() override
Actual entry.
bool lattice_force(const Def *concr, const Def *abstr)
Non-monotone write of concr ↦ abstr into lattice() and map().
const Def * rewrite(const Def *) override
Rewrites def and then maps the result to its repr().
const Def * rewrite_imm_Proxy(const Proxy *proxy) override
By default: ignore Proxys.
Analysis(World &world, std::string name)
void make_dense()
Forces whole-World rounds unconditionally.
virtual void leave()
Called after curr_mut() has been completely dealt with.
bool lattice(const Def *concr, const Def *abstr)
Writes concr ↦ abstr into lattice() and map().
void taint(Def *mut)
Additionally schedules mut for the next sparse round.
virtual void rewrite_annex(flags_t, Sym, const Def *)
const Def * lattice(const Def *def) const
bool is_top(const Def *def) const
virtual void rewrite_external(Def *)
bool is_sparse() const
Does the current round only re-drain last round's dirty mutables?
virtual void reset()
Clears the rewriter map and resets Phase::todo() and is_bootstrapping() for the next fixed-point iter...
virtual void finalize()
Run after the main analysis - only in full rounds, so it always sees the complete abstract World.
const auto & lattice() const
The whole map; used e.g. to diff two fixed-point runs.
bool pin(const Def *def)
Monotonically forces def to ⊤ (keep as is).
bool is_bootstrapping() const
< Disambiguates the Phase/Rewriter double base; for an Analysis both denote the same World.
Analysis(World &world, flags_t annex)
const Def * repr(const Def *def) const
The representative of def: follows def ↦ lattice(def) to the end of its chain.
Def * rewrite_mut(Def *) override
Schedules mut for a breadth-first visit of its dependencies and records mut -> mut.
const Def * uncurry_callee() const
Cleanup(World &world, flags_t annex)
void start() override
Actual entry.
ClosedMutPhase(World &world, flags_t annex, bool elide_empty, bool schedule=false)
virtual void visit(M *)=0
ClosedMutPhase(World &world, std::string name, bool elide_empty, bool schedule=false)
World & world() const noexcept
Some "global" variables needed all over the place.
const Def * rewrite_mut(Def *mut) override
InplaceRWPhase(World &world, std::string name, Analysis *analysis=nullptr)
bool rewrite_annexes() const override
InplaceRWPhase(World &world, flags_t annex, Analysis *analysis=nullptr)
void rewrite_annex(flags_t, Sym, const Def *) override
void rewrite_external(Def *) override
virtual void visit(const Nest &)=0
const Nest & nest() const
NestPhase(World &world, std::string name, bool elide_empty, bool schedule=false)
NestPhase(World &world, flags_t annex, bool elide_empty, bool schedule=false)
Builds a nesting tree for all mutables/binders.
Organizes several Phases into a pipeline.
PhaseMan(World &world, flags_t annex)
void start() final
Actual entry.
const auto & phases() const
void apply(bool, Phases &&)
A Phase performs one self-contained task over the whole World.
virtual std::unique_ptr< Phase > recreate()
Creates a new instance; needed by a fixed-point PhaseMan.
static void hook(Flags2Phases &phases)
void invalidate(bool todo=true)
Signals that another round of fixed-point iteration is required, either as part of.
const fe::Log & log() const
Phase(World &world, std::string name)
static void run(Args &&... args)
Runs a single Phase.
static std::unique_ptr< Phase > create(const Flags2Phases &phases, const Def *def)
void profile_count(std::string_view key, uint64_t n=1)
Adds n to the custom fe::Profiler counter key of the current run; no-op unless profiling is enabled.
virtual std::unique_ptr< Phase > take_resolved()
The Phase to use instead; nullptr means elide.
virtual void apply(Phase &)
Dito, but invoked by Phase::recreate.
const fe::Vector< std::string > & args()
Command-line arguments passed to this Phase's plugin via -X <plugin>:<arg>.
virtual void run()
Entry point and generates some debug output; invokes Phase::start.
std::string_view name() const
virtual void start()=0
Actual entry.
virtual void apply(const App *)
Invoked if your Phase has additional args.
virtual bool redirects() const
If true, Phase::create uses take_resolved().
Used as intermediate value during optimizatinos such as Analysis.
virtual void finalize()
Run after all roots have been walked - but for an RWPhase still before the two worlds are swapped.
const Def * lattice(const Def *def) const
Returns the abstract value computed by the associated Analysis for def, or nullptr if no value is ava...
virtual void rewrite_external(Def *)=0
const Analysis * analysis() const
RWBase(World &world, flags_t annex, Analysis *analysis, std::unique_ptr< World > &&new_world)
void start() override
Actual entry.
RWBase(World &world, std::string name, Analysis *analysis)
RWBase(World &world, flags_t annex, Analysis *analysis)
virtual bool rewrite_annexes() const =0
bool is_bootstrapping() const
Returns whether we are currently bootstrapping (rewriting annexes).
virtual const Def * rewrite_root(const Def *def)
Rewrites a root - i.e. an annex or an external.
const Def * abstracted(const Def *def) const
Returns lattice(def) if it differs from def (i.e. we learned something), otherwise nullptr.
virtual bool analyze()
Runs the optional pre-analysis on Phase::world, typically to a fixed point, before rewriting begins.
RWBase(World &world, std::string name, Analysis *analysis, std::unique_ptr< World > &&new_world)
Rewrite the World of Phase::world into new_world.
virtual void rewrite_annex(flags_t, Sym, const Def *)=0
World & new_world()
Create new Defs into this.
RWPhase(World &world, flags_t annex, Analysis *analysis=nullptr)
void rewrite_annex(flags_t, Sym, const Def *) override
RWPhase(World &world, std::string name, Analysis *analysis=nullptr)
void start() override
RWBase::start() and then swaps the two worlds.
bool rewrite_annexes() const final
An RWPhase has to walk the annexes: it must re-create every one of them to populate new_world()'s tab...
World & world()=delete
Hides both and forbids direct access.
void rewrite_external(Def *) override
World & old_world()
Get old Defs from here.
const Def * rewrite(const Def *def) final
virtual const Def * replace(const Def *def)=0
Repl(World &world, flags_t annex)
World & world()
replace() inspects and builds Defs of the old world; the RWPhase machinery carries the result over.
virtual const Def * map(const Def *old_def, const Def *new_def)
Rewriter(std::unique_ptr< World > &&ptr)
virtual const Def * rewrite(const Def *)
The World represents the whole program and manages creation of MimIR nodes (Defs).
const fe::Log & log() const
Log via log().e("...", args) etc.; owned by the Driver.
DefMap< const Def * > Def2Def
absl::flat_hash_map< flags_t, std::function< std::unique_ptr< Phase >(World &)> > Flags2Phases
Maps an axiom of a Phase to a function that creates one.
std::deque< std::unique_ptr< Phase > > Phases
static consteval flags_t base()