18using Phases = std::deque<std::unique_ptr<Phase>>;
23class Phase :
public fe::RuntimeCast<Phase> {
34 virtual std::unique_ptr<Phase>
recreate();
50 template<
class A,
class P>
60 const fe::Log&
log()
const {
return world_.log(); }
66 const fe::Vector<std::string>&
args();
71 bool todo()
const {
return todo_; }
88 template<
class P,
class... Args>
90 P p(std::forward<Args>(
args)...);
146 virtual void reset();
165 const auto&
lattice()
const {
return lattice_; }
169 if (
auto abstr = fe::lookup(lattice_, def))
return abstr;
182 assert(!is_cyclic(def) &&
"lattice chain must not loop");
183 for (
auto next = follow(def); next != def; def = next, next = follow(def)) {}
189 auto i = lattice_.find(def);
190 return i != lattice_.end() && i->second == def;
213 if (!lattice_.erase(concr))
return false;
214 return touch(),
true;
232 if (!visited.emplace(def).second)
return;
233 if (pinnable(def))
pin(def);
235 for (
auto d : def->
deps())
241 pin_imm(visited, def, pinnable);
265 void start()
override;
289 const Def* rewrite_mut_##N(N* mut) override { return mut; }
297 const Def* follow(
const Def* def)
const {
298 auto i = lattice_.find(def);
299 return i == lattice_.end() ? def : i->second;
302 bool is_cyclic(
const Def* def)
const;
308 std::deque<Def*> worklist_;
311 bool nonlocal_ =
false;
312 bool curr_sparse_ =
false;
314 size_t num_drained_ = 0;
347 const Def*
lattice(
const Def* old_def)
const {
return analysis_ ? analysis_->lattice(old_def) :
nullptr; }
352 return l && l != old_def ? l :
nullptr;
355 bool is_top(
const Def* old_def)
const {
return analysis_->is_top(old_def); }
383 void start()
override;
400 return (type->isa<
Sigma>() || type->isa_imm<
Arr>()) && type->num_tprojs() != 1;
422 const Def* eta_coerce(
const Def* type,
const Def* val);
425 const Def* reshape(
DefVec&
todo,
const Def* type);
427 DefMap<Def2Def> etas_;
430 std::unique_ptr<World> new_world_;
447 while (
auto subst =
replace(def))
454#define MIM_CONCAT_INNER(a, b) a##b
455#define MIM_CONCAT(a, b) MIM_CONCAT_INNER(a, b)
457#define MIM_REPL(__phases, __annex, ...) MIM_REPL_IMPL(__phases, __annex, __LINE__, __VA_ARGS__)
460#define MIM_REPL_IMPL(__phases, __annex, __id, ...) \
461 struct MIM_CONCAT(Repl_, __id) : ::mim::Repl { \
464 const ::mim::Def* replace(const ::mim::Def* def) final __VA_ARGS__ \
466 ::mim::Phase::hook<__annex, MIM_CONCAT(Repl_, __id)>(__phases)
495 const auto&
phases()
const {
return phases_; }
510template<class M =
Def>
530 M*
root()
const {
return root_; }
533 const bool elide_empty_;
534 const bool schedule_;
540template<
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.
const Def * lookup(const Def *) override
Lookup old_def by searching in reverse through the stack of maps.
const Def * rewrite_mut(Def *) final
Schedules mut for a breadth-first visit of its dependencies and records mut -> mut.
void pin_imm(const Def *def, DefPred auto pinnable)
virtual void leave()
Called after curr_mut() has been completely dealt with.
void taint(Def *mut)
Additionally schedules mut for the next sparse round.
void touch()
Observable analysis information changed: records curr_mut() as dirty - the seed set of the next spars...
const Def * lattice(const Def *def) const
bool is_top(const Def *def) const
void pin_imm(DefSet &visited, const Def *def, DefPred auto pinnable)
pins every def in def's immutable subgraph for which pinnable holds.
virtual void rewrite_external(Def *)
bool lattice_erase(const Def *concr)
Non-monotone removal of concr from lattice(), so no later sparse round replays it.
virtual void reset()
Clears the rewriter map and resets Phase::todo() for the next fixed-point iteration.
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.
static bool is_singleton(const Def *def)
The sole inhabitant of a singleton type is ⊤ and shared by all binders of that type,...
bool pin(const Def *def)
Monotonically forces def to ⊤ (keep as is).
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.
A (possibly paramterized) Array.
Removes unreachable and dead code by rebuilding the whole World into a new one and swapping them afte...
RWPhase(World &world, std::string name, Analysis *analysis=nullptr)
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)
Defs deps() const noexcept
World & world() const noexcept
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Some "global" variables needed all over the place.
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.
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.
static std::unique_ptr< Phase > create(const Flags2Phases &phases, const Def *def)
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 bool analyze()
Runs the optional pre-analysis on old_world(), typically to a fixed point, before rewriting begins.
World & new_world()
Create new Defs into this.
virtual void finalize()
Run after all roots have been walked but still before the two worlds are swapped.
const Def * lookup(const Def *) override
Lookup old_def by searching in reverse through the stack of maps.
RWPhase(World &world, flags_t annex, Analysis *analysis=nullptr)
const Def * rewrite_imm_Tuple(const Tuple *) override
static bool splittable(const Def *type)
Does coerce() explode and reassemble values of type?
const Def * abstracted(const Def *old_def) const
Returns lattice(old_def) if it differs from old_def (i.e. we learned something), otherwise nullptr.
const Def * coerce(const Def *new_type, const Def *new_val)
Converts the new new_val into a value of the new new_type, where a checking position demands it.
RWPhase(World &world, std::string name, Analysis *analysis=nullptr)
void start() override
Actual entry.
bool is_top(const Def *old_def) const
const Def * rewrite_stub(Def *, Def *) override
World & world()=delete
Hides both and forbids direct access.
const Analysis * analysis() const
const Def * lattice(const Def *old_def) const
Returns the abstract value computed by the associated Analysis for the given old-world Def,...
World & old_world()
Get old Defs from here.
virtual void rewrite_external(Def *)
const Def * rewrite_imm_App(const App *) override
An RWPhase that searches for a pattern and replaces it.
const Def * rewrite(const Def *def) final
virtual const Def * replace(const Def *def)=0
RWPhase(World &world, std::string name, Analysis *analysis=nullptr)
World & world()
replace() inspects and builds Defs of the old world; the RWPhase machinery carries the result over.
Recurseivly rebuilds part of a program into the provided World w.r.t. Rewriter::map.
virtual const Def * rewrite_stub(Def *, Def *)
virtual const Def * rewrite(const Def *)
Data constructor for a Sigma.
The World represents the whole program and manages creation of MimIR nodes (Defs).
const Def * inhabitant(const Def *type)
The sole inhabitant of type, if it has exactly one, nullptr otherwise.
Used by Def::proj & friends.
DefMap< const Def * > Def2Def
fe::Vector< const Def * > DefVec
GIDSet< const Def * > DefSet
std::deque< std::unique_ptr< Phase > > Phases
ankerl::unordered_dense::map< flags_t, std::function< std::unique_ptr< Phase >(World &)> > Flags2Phases
Maps an axiom of a Phase to a function that creates one.
static consteval flags_t base()