8#include <fe/worklist.h>
14using namespace std::literals;
17template void fe::Patricia<const mim::Var, mim::DefKey>::Set::dump()
const;
18template void fe::Patricia<mim::Def, mim::DefKey>::Set::dump()
const;
23std::ostream&
DefKey::stream(std::ostream& os,
const Def* d) {
return os << d->sym() <<
": " << d->gid(); }
37 , num_ops_(
ops.size())
45 auto h = fe::hash_combine(0,
u64(
node) << 32 | (
type ?
type->gid() : 0));
46 h = fe::hash_combine(h,
flags_);
52 for (
size_t i = 0, e =
ops.size(); i != e; ++i) {
55 gids = gids << 32 |
op->
gid();
56 if (i & 1) h = fe::hash_combine(h, gids), gids = 0;
59 if (gids) h = fe::hash_combine(h, gids);
68 auto collect = [&](
const Def* d,
bool with_vars =
true,
bool with_muts =
true) {
69 auto dvars = with_vars ? d->local_vars() :
Vars();
70 auto dmuts = with_muts ? d->local_muts() :
Muts();
71 if ((
vars && dvars) || (muts && dmuts)) {
75 if (dvars)
vars = dvars;
76 if (dmuts) muts = dmuts;
104 std::fill_n(ops_ptr(),
num_ops,
nullptr);
117 vars_ =
Vars(as<Var>());
121Nat::Nat(
World& world)
124LMax::LMax(World& world,
Defs ops)
125 : Def(Node, world.type_level(), ops, 0) {}
132 if (!
is_set())
return false;
146Defs Def::reduce_(
const Def* arg)
const {
147 if (!
is_set()) fe::throwf(
"cannot reduce `{}`: it is not set",
this);
150 assert(off !=
size_t(-1) &&
"not a binder");
151 return {
ops().begin() + off,
num_ops() - off};
158void Def::watch()
const {
159#ifdef MIM_ENABLE_CHECKS
164Def* Def::finalize() {
173 size_t n =
ops.size();
174 assert(n ==
num_ops() &&
"num ops don't match");
176 for (
size_t i = 0; i != n; ++i) {
192 assert(def && !
op(i) && curr_op_++ == i);
195 if (i + 1 ==
num_ops())
return finalize();
210 std::fill_n(ops_ptr(),
num_ops(),
nullptr);
219 if (var_)
return var_;
224 if (
auto var = def->isa<Var>()) {
226 return {
var, std::nullopt};
228 if (
auto ex = def->isa<
Extract>()) {
229 if (
auto var = ex->tuple()->isa<Var>()) {
232 return {
var, std::nullopt};
255 if (
auto mut =
isa_mut())
return mut->free_vars();
260 fvs =
vars.merge(fvs, mut->free_vars());
268 if (
auto mut = def->
isa_mut())
return f(mut->free_vars());
272 if (f(mut->free_vars()))
return true;
298 for (
bool todo = cyclic; todo;) {
318 if (mark_ != 0 && mark_ !=
run - 1) {
319 if constexpr (
init) todo |= mark_ ==
run;
327 auto& muts =
w.muts();
328 auto&
vars =
w.vars();
334 if constexpr (
init) mut->muts_ = muts.insert(mut->muts_,
this);
335 fvs =
vars.merge(fvs, mut->free_vars<init>(w, todo, run));
341 if constexpr (!
init) todo |= fvs0 != fvs;
346void Def::invalidate() {
350 for (
auto mut :
users())
359 assert((!
is_external() || closed) &&
"an external must not have free Vars");
365 if (fvs.empty())
return isa_mut();
367 return fvs.min()->binder()->outermost_binder();
373 if (fvs.contains(
var))
return true;
374 if (
auto [_, ins] = checked.emplace(mut); !ins)
return false;
377 if (this->
nests(fv->binder(), checked))
return true;
383 if (!this->
has_var())
return false;
385 return this->
nests(mut, checked);
390 if (!this->
has_var())
return false;
394 if (this->
nests(fv->binder(), checked))
return true;
400 return std::ranges::all_of(
free_vars(), [mut](
const Var* fv) {
401 auto binder = fv->
binder();
402 return binder == mut || binder->
nests(mut);
415 auto& e =
error().e(
loc,
"expected {}", what);
416 if (!
loc) e.n(
"got `{}`",
this);
422 if (
auto loc = w.get_loc())
return loc;
426 auto queue = fe::BFSWorklist<DefSet>{
this};
430 constexpr size_t Budget = 512;
431 auto budget = Budget;
433 while (!queue.empty()) {
434 for (
auto dep : queue.pop()->
deps()) {
435 if (budget-- == 0)
return {};
436 if (!queue.push(
dep))
continue;
456 auto mine =
dbg(), merged = mine;
457 if (!merged.loc()) merged.set(d.loc());
458 if (!merged.sym()) merged.set(d.sym());
459 if (!(merged == mine))
set_dbg(merged);
465 if (ow || !dbg_ || dbg_ == key)
return void(dbg_ = key);
470 if (
auto t =
type())
return t;
472 if (
auto t = isa<Type>())
return w.type(w.lsucc(t->level()));
473 assert(isa<Level>());
478 static constexpr std::string_view Names[Num_Nodes] = {
479#define CODE(node, _) #node,
489 assert(isa<Level>() || isa<Type>() || isa<Var>());
494 bool set = num_ops_ == 0 || ops_ptr()[num_ops_ - 1];
495 return Defs(ops_ptr() - 1, (
set ? num_ops_ : 0) + 1);
500 if (
auto u = t->type())
515 if (a == b)
return Cmp::E;
525 if (a->is_mutable())
return Cmp::U;
527 if (
auto va = a->isa<
Var>()) {
528 auto vb = b->as<
Var>();
530 auto mb = vb->binder();
532 if (mb->is_set() && mb->free_vars().contains(va))
return Cmp::G;
537 for (
size_t i = a->num_ops(); i-- != 0;)
538 if (
auto res =
cmp(a->op(i), b->
op(i)); res ==
Cmp::L || res ==
Cmp::G)
return res;
540 return cmp(a->type(), b->
type());
544bool Def::cmp_(
const Def* a,
const Def* b) {
545 auto res =
cmp(a, b);
547 a->world().log().w(
"commute check resorts to an unstable gid-based compare");
548 return c ==
Cmp::L ? a->gid() < b->
gid() : a->gid() > b->
gid();
554bool Def::less (
const Def* a,
const Def* b) {
return cmp_<Cmp::L>(a, b); }
555bool Def::greater(
const Def* a,
const Def* b) {
return cmp_<Cmp::G>(a, b); }
572 return is_immutabilizable() ? w.pi(pi->dom(), pi->codom(), pi->is_implicit()) :
nullptr;
581 auto seq = as<Seq>();
584 return arr ? w.arr(seq->shape(), seq->body()) : w.pack(seq->shape(), seq->body());
586 if (
auto n = seq->shape().is_fused() ? std::nullopt :
Lit::isa(seq->arity());
587 n && *n < w.flags().scalarize_threshold) {
588 auto elems =
DefVec(*n, [&](
size_t i) {
return seq->reduce(w.lit_idx(*n, i)); });
589 return arr ? w.sigma(elems) : w.tuple(elems);
618 if (
auto t =
type(); t && !t->isa<
Type>())
return t->arity();
629 assert(this->
sym() == to->
sym());
637 if (def->isa<
Seq>())
return true;
649 assert(i == 0 &&
"only inhabitant of Idx 2 is 0_1");
655 if (
auto seq = isa<Seq>()) {
657 if (seq->shape().is_fused())
return w.peel(seq, w.lit_idx(a, i));
658 return seq->reduce(w.lit_idx(a, i));
661 if (isa<Prod>())
return op(i);
671 if (
auto app = def->isa<
App>())
672 if (app->callee()->isa<Idx>())
return app->arg();
Loc err_loc() const
Returns a blame Loc from World::get_loc, this Def, or its nearest located dependency,...
void set_dbg(Dbg) const
Interns dbg via Driver::dbg and stores the key in Def::dbg_.
const Def * proj(nat_t a, nat_t i) const
Similar to World::extract while assuming an arity of a, but also works on Sigmas and Arrays.
constexpr Node node() const noexcept
Def * set(size_t i, const Def *)
Successively set from left to right.
Defs deps() const noexcept
nat_t num_tprojs() const
As above but yields 1, if an Arr/Pack exceeds Flags::scalarize_threshold.
const Def * zonk() const
If Holes have been filled, reconstruct the program without them.
World & world() const noexcept
Def * set_type(const Def *)
Update type.
std::string_view node_name() const
fe::Error & error() const noexcept
constexpr auto ops() const noexcept
bool in_scope(Def *mut) const
Is every free Var of this bound by mut or by a mutable that nests mut?
Vars local_vars() const
Vars reachable by following immutable deps().
size_t reduction_offset() const noexcept
First Def::op that needs to be dealt with during reduction; e.g.
constexpr flags_t flags() const noexcept
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
bool is_term() const
Is this Def a term, i.e. is its type() a Type?
const Def * debug_prefix(std::string) const
const Def * op(size_t i) const noexcept
bool is_immutabilizable()
const Def * var(nat_t a, nat_t i) noexcept
void transfer_external(Def *to)
const Def * unfold_type() const
Yields the type of this Def and builds a new Type (LSucc n) if necessary.
bool has_free_vars() const
Same as !free_vars().empty().
bool has_free_var(const Var *) const
Same as free_vars().contains(var).
auto vars(Projector auto f) noexcept
void set_dbg_(Dbg, bool ow) const
Backs Def::set(Dbg).
Muts local_muts() const
Mutables reachable by following immutable deps(); mut->local_muts() is by definition the set { mut }...
const Def * debug_suffix(std::string) const
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Def * outermost_binder() const
Transitively walks up free_vars() till the outermoust binder has been found.
bool nests(Def *mut)
Does this nest mut?
const Def * dep(size_t i) const noexcept
bool is_mutable() const noexcept
bool is_external() const noexcept
constexpr MutNode mut_node() const noexcept
node() of a Def whose Node may be mutable, whether this one is or not; see MutNode.
static bool less(const Def *a, const Def *b)
static bool greater(const Def *a, const Def *b)
void bail_expected(fe::Cite what) const
Blames this for not being what and bails; where Def::expect and friends funnel their diagnostic.
const Def * var()
Not necessarily a Var: E.g., if the return type is [], this will yield ().
static Cmp cmp(const Def *a, const Def *b)
void collect_locals()
Fills in an immutable's local Vars and Muts once it is in the sea.
Driver & driver() const noexcept
nat_t num_projs() const
Yields Def::arity(), if it is a Lit, or 1 otherwise.
const Def * immutabilize()
const Def * var_type()
If this is a binder, compute the type of its Variable.
constexpr u32 gid() const noexcept
Global id - unique number for this Def.
const Def * arity() const
Number of elements available to Extract / Insert (may be dynamic).
Def * unset()
Unsets all Def::ops; works even, if not set at all or only partially set.
std::string unique_name() const
name + "_" + Def::gid
void set_dbg_key_(DbgKey, bool ow) const
Backs Def::set(DbgKey).
Muts users()
Set of mutables where this mutable is locally referenced.
bool is_closed() const
Same as !has_free_vars().
Vars free_vars() const
Global set of free Vars: extends local_vars() by transitively following mutables as well.
Dbg dbg() const
Looks up Def::dbg_ in Driver::dbg.
const Def * check(size_t i, const Def *def)
Checks whether the ith operand can be set to def.
const Var * has_var()
Only returns not nullptr, if Var of this mutable has ever been created.
bool has_hole() const noexcept
Is this a Hole or does it reach one through Def::deps() up to (and excluding) the next mutable?
bool has_free_vars_in(Vars) const
Same as vars.has_intersection(free_vars()).
constexpr size_t num_ops() const noexcept
Some "global" variables needed all over the place.
This node is a hole in the IR that is inferred by its context later on.
static constexpr nat_t size2bitwidth(nat_t n)
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
static std::optional< nat_t > isa_lit(const Def *def)
static std::optional< T > isa(const Def *def)
static constexpr flags_t Muts_Bit
static constexpr flags_t Vars_Bit
Base class for Arr and Pack.
constexpr Shape() noexcept=default
A variable introduced by a binder (mutable).
static std::pair< const Var *, std::optional< nat_t > > isa_tproj(const Def *def)
Matches a Var v itself or an Extract v#j of it.
Def * binder() const
The binder of this Var.
The World represents the whole program and manages creation of MimIR nodes (Defs).
Flags & flags()
Retrieve compile Flags.
const Def * extract(const Def *d, const Def *i)
Sym sym(std::string_view)
const Def * var(Def *mut)
const Externals & externals() const
const Def * type_indices(Shape shape)
The type of an index into shape: Idx n, a Sigma of those, or «i: r; Idx (s#i)» for a dynamic rank.
const Lit * lit_nat(nat_t a)
Defs reduce(const Var *var, const Def *arg)
Yields the new body of [mut->var() -> arg]mut.
fe::View< const Def * > Defs
static bool is_seq_arity(const Def *def)
fe::Vector< const Def * > DefVec
fe::Patricia< const Var, DefKey >::Set Vars
constexpr bool is_mut_node(Node node) noexcept
fe::Patricia< Def, DefKey >::Set Muts
static bool any_free_vars(const Def *def, std::predicate< Vars > auto f)
static std::ostream & stream(std::ostream &, const Def *)
uint64_t scalarize_threshold