28 world.
log().d(
"apply phase {}", p_def);
30 if (
auto axm = p_def->isa<
Axm>())
31 if (
auto i = phases.find(axm->flags()); i != phases.end()) {
32 auto phase = i->second(
world);
34 phase->apply(def->isa<
App>());
35 if (phase->redirects())
return phase->take_resolved();
39 fe::throwf(
"no phase registered for stage `{}`; is its plugin loaded?", axm->sym());
41 fe::throwf(
"unsupported callee for a phase: `{}`", p_def);
46 auto ptr = (*ctor)(
world());
78 curr_sparse_ = !dense_ && !nonlocal_ && !dirty_.empty();
80 auto seeds = fe::Vector<Def*>(dirty_.begin(), dirty_.end());
86 log().v(
"sparse round: re-drain {} dirty muts", seeds.size());
93 old2new().reserve(lattice_.size());
94 for (
auto [concr, abstr] : lattice_)
95 if (!concr->isa_mut())
map(concr, abstr);
97 for (
auto mut : seeds)
101 for (
auto mut :
world().externals().muts())
108 profile_count(curr_sparse_ ?
"rounds.sparse" :
"rounds.full");
109 profile_count(
"muts.drained", std::exchange(num_drained_,
size_t(0)));
127 if (
auto [i, ins] = lattice_.emplace(concr, abstr); !ins) {
128 if (i->second == abstr)
return false;
131 return touch(),
true;
138 if (
auto [i, ins] = lattice_.emplace(concr, abstr); !ins) {
139 assert((i->second != concr || abstr == concr) &&
"monotonicity violation: must not descend from ⊤");
140 if (i->second == abstr)
return false;
142 }
else if (concr == abstr) {
145 return touch(),
true;
148bool Analysis::is_cyclic(
const Def* def)
const {
149 auto slow = follow(def), fast = follow(slow);
150 for (; slow != fast; slow = follow(slow), fast = follow(follow(fast))) {}
151 return follow(fast) != fast;
160 if (
lookup(mut))
return mut;
162 worklist_.emplace_back(mut);
166void Analysis::drain() {
167 while (!worklist_.empty()) {
168 auto mut = worklist_.front();
169 worklist_.pop_front();
173 log().d(
"enter {}", mut);
174 for (
auto d : mut->deps())
187 for (uint32_t i = 0;
todo; ++i) {
188 if (i >= max_iters) fe::throwf(
"phase `{}` did not reach a fixed point after {} iterations",
name(), max_iters);
189 log().v(
"iteration {}", i);
194 for (
auto mut :
old_world().externals().muts())
206 return analysis_->todo();
220 if (
auto new_mut =
rewrite(old_mut)->isa_mut(); new_mut && !new_mut->
is_external()) new_mut->externalize();
227 if (
auto pi = callee->isa_type<
Pi>()) arg =
coerce(pi->dom(), arg);
233 auto& w = tuple->
world();
245 for (
size_t i = 0; i != n; ++i)
251 auto old_lam = old_mut->isa<
Lam>();
252 auto new_lam = new_mut->isa<
Lam>();
255 map(old_mut, new_mut);
256 auto _ =
enter(old_mut);
257 auto pi = new_lam->
type();
261 return seal(old_mut, new_mut);
265 if (val->
type() == type)
return val;
267 auto res = reshape(
todo, type);
270 if (!
todo.empty()) fe::throwf(
"cannot coerce `{}` to `{}`", val, type);
274const Def* RWPhase::reshape(
DefVec& todo,
const Def* type) {
276 auto front =
todo.empty() ? nullptr :
todo.back();
279 if (
auto res = eta_coerce(type, front))
return todo.pop_back(), res;
282 if (
auto s = w.inhabitant(type))
return s;
283 if (front && w.inhabitant(front->type()))
return todo.pop_back(), reshape(
todo, type);
286 auto n = type->num_tprojs();
288 for (
size_t i = 0; i != n; ++i)
289 parts.emplace_back(reshape(
todo, w.component(type, parts, n, i)));
290 return w.tuple(type, parts);
293 if (front && front->type()->num_tprojs() != 1) {
295 auto pieces = front->tprojs();
296 todo.insert(
todo.end(), pieces.rbegin(), pieces.rend());
297 return reshape(
todo, type);
300 fe::throwf(
"cannot coerce to `{}`", type);
303const Def* RWPhase::eta_coerce(
const Def* type,
const Def* val) {
304 auto dst =
type->isa<
Pi>();
305 auto src = val->type()->isa<
Pi>();
306 if (!dst || !src)
return nullptr;
307 auto& memo = etas_[
type][val];
308 if (memo)
return memo;
312 auto arg =
coerce(src->dom(), eta->var());
313 auto codom = dst->reduce(eta->var());
314 log().d(
"coerce {}: {} → {}", val, src, dst);
324 phases_ = std::move(
phases);
325 name_ += fixed_point_ ?
" tt" :
" ff";
332 for (
auto arg :
args->projs())
339 auto& man =
static_cast<PhaseMan&
>(phase);
341 for (
auto& old_phase : man.phases())
342 new_phases.emplace_back(old_phase->recreate());
343 apply(man.fixed_point(), std::move(new_phases));
351 auto all = fe::Bitset(n,
true);
353 auto ran = fe::Bitset(n,
false);
355 for (uint32_t iter = 0; stale.any(); ++iter) {
356 if (iter >= max_iters)
357 fe::throwf(
"phase `{}` did not reach a fixed point after {} iterations",
name(), max_iters);
361 for (
size_t i = 0; i != n; ++i) {
362 auto& phase =
phases()[i];
363 if (!stale.test(i)) {
364 log().v(
"skip `{}`: World unchanged since its last quiet run", phase->name());
369 if (ran.test(i)) phase = phase->recreate();
virtual void prepare()
Run before the main analysis.
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 * 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.
virtual void leave()
Called after curr_mut() has been completely dealt with.
void touch()
Observable analysis information changed: records curr_mut() as dirty - the seed set of the next spars...
virtual void rewrite_external(Def *)
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,...
const Def * repr(const Def *def) const
The representative of def: follows def ↦ lattice(def) to the end of its chain.
const Def * callee() const
static auto uncurry_args(const Def *def)
const Def * uncurry_callee() const
static const Def * coerce(const Def *type, const Def *value)
Can value be assigned to sth of type?
World & world() const noexcept
constexpr auto ops() const noexcept
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
const Def * var(nat_t a, nat_t i) noexcept
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
bool is_external() const noexcept
constexpr auto reduce(const Def *arg) const
const Var * has_var()
Only returns not nullptr, if Var of this mutable has ever been created.
const fe::Vector< std::string > & args(std::string_view plugin) const
Yields an empty fe::Vector if plugin has none.
fe::Profiler & profiler()
auto phase(flags_t flags)
Lam * set(Filter filter, const Def *body)
static std::optional< T > isa(const Def *def)
static T as(const Def *def)
PhaseMan(World &world, flags_t annex)
void start() final
Actual entry.
void apply(bool, Phases &&)
virtual std::unique_ptr< Phase > recreate()
Creates a new instance; needed by a fixed-point PhaseMan.
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)
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.
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.
A dependent function type.
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.
const Def * rewrite_imm_Tuple(const Tuple *) override
static bool splittable(const Def *type)
Does coerce() explode and reassemble values of type?
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.
void start() override
Actual entry.
const Def * rewrite_stub(Def *, Def *) override
World & old_world()
Get old Defs from here.
virtual void rewrite_external(Def *)
const Def * rewrite_imm_App(const App *) override
fe::Vector< Scope > scopes_
friend void swap(Rewriter &rw1, Rewriter &rw2) noexcept
virtual const Def * rewrite_mut(Def *)
virtual const Def * rewrite_stub(Def *, Def *)
const Def * map(const Def *old_def, const Def *new_def)
const Def * seal(Def *old_mut, Def *new_mut)
The tail of rewrite_stub: immutabilizes new_mut in hindsight, as rewriting may have made it vacuous.
virtual const Def * rewrite(const Def *)
auto enter(Def *new_mut)
Updates curr_mut() to new_mut and restores it at the end of the scope.
void push(Vars old_vars={}, Vars new_vars={})
Def2Def & old2new()
The map of the innermost push() scope.
virtual const Def * lookup(const Def *old_def)
Lookup old_def by searching in reverse through the stack of maps.
Data constructor for a Sigma.
static const Def * infer(World &, Defs)
const flags_t * opaque(const Def *d) const
Like flags() but only for an Axm or a non-Lam mutable - whose identity phases must preserve.
The World represents the whole program and manages creation of MimIR nodes (Defs).
const Def * app(const Def *callee, const Def *arg)
u32 curr_gid() const
Manage global identifier - a unique number for each Def.
World & verify()
Verifies that all externals() and annexes() are Def::is_closed(), if MIM_ENABLE_CHECKS.
const fe::Log & log() const
Log via log().e("...", args) etc.; owned by the Driver.
const Def * tuple(Defs ops)
Lam * mut_lam(const Pi *pi)
fe::View< const Def * > Defs
static bool has_inferred_type(const Tuple *tuple)
Is tuple's type what World::tuple would infer from its ops?
fe::Vector< const Def * > DefVec
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 std::string demangle(plugin_t plugin)
Reverts an Axm::mangled plugin back to its name; never longer than Annex::Max_Plugin_Size.
static constexpr plugin_t flags2plugin(flags_t f)