MimIR 0.1
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
def.h
Go to the documentation of this file.
1#pragma once
2
3#include <optional>
4
5#include <fe/assert.h>
6#include <fe/cast.h>
7#include <fe/enum.h>
8
9#include "mim/config.h"
10
11#include "mim/util/dbg.h"
12#include "mim/util/hash.h"
13#include "mim/util/pool.h"
14#include "mim/util/util.h"
15#include "mim/util/vector.h"
16
17// clang-format off
18#define MIM_NODE(m) \
19 m(Type, type) m(Univ, univ) m(UMax, umax) m(UInc, uinc) \
20 m(Pi, pi) m(Lam, lam) m(App, app) \
21 m(Sigma, sigma) m(Tuple, tuple) m(Extract, extract) m(Insert, insert) \
22 m(Arr, arr) m(Pack, pack) \
23 m(Join, join) m(Vel, vel) m(Test, test) m(Top, top) \
24 m(Meet, meet) m(Ac, ac ) m(Pick, pick) m(Bot, bot) \
25 m(Proxy, proxy) \
26 m(Axiom, axiom) \
27 m(Lit, lit) \
28 m(Nat, nat) m(Idx, idx) \
29 m(Var, var) \
30 m(Infer, infer) \
31 m(Global, global) \
32 m(Uniq, Uniq)
33// clang-format on
34
35namespace mim {
36
37namespace Node {
38
39#define CODE(node, name) node,
40enum : node_t { MIM_NODE(CODE) };
41#undef CODE
42
43#define CODE(node, name) +size_t(1)
44constexpr auto Num_Nodes = size_t(0) MIM_NODE(CODE);
45#undef CODE
46
47} // namespace Node
48
49class App;
50class Axiom;
51class Var;
52class Def;
53class World;
54
55/// @name Def
56/// GIDSet / GIDMap keyed by Def::gid of `conset Def*`.
57///@{
58template<class To> using DefMap = GIDMap<const Def*, To>;
63///@}
64
65/// @name Def (Mutable)
66/// GIDSet / GIDMap keyed by Def::gid of `Def*`.
67///@{
68template<class To> using MutMap = GIDMap<Def*, To>;
72///@}
73
74/// @name Var
75/// GIDSet / GIDMap keyed by Var::gid of `const Var*`.
76///@{
77template<class To> using VarMap = GIDMap<const Var*, To>;
81///@}
82
83//------------------------------------------------------------------------------
84
85/// Helper class to retrieve Infer::arg if present.
86class Ref {
87public:
88 Ref() = default;
89 Ref(const Def* def)
90 : def_(def) {}
91
92 const Def* operator*() const { return refer(def_); }
93 const Def* operator->() const { return refer(def_); }
94 operator const Def*() const { return refer(def_); }
95 explicit operator bool() const { return def_; }
96 static const Def* refer(const Def* def); ///< Same as Infer::find but does nothing if @p def is `nullptr`.
97 const Def* def() const { return def_; } ///< Retrieve wrapped Def without Infer::refer%ing.
98
99 friend std::ostream& operator<<(std::ostream&, Ref);
100
101private:
102 const Def* def_ = nullptr;
103};
104
105using NormalizeFn = Ref (*)(Ref, Ref, Ref);
106
107//------------------------------------------------------------------------------
108
109// TODO remove or fix this
110enum class Sort { Term, Type, Kind, Space, Univ, Level };
111
112//------------------------------------------------------------------------------
113
114using fe::operator&;
115using fe::operator|;
116using fe::operator^;
117using fe::operator<=>;
118using fe::operator==;
119using fe::operator!=;
120
121/// @name Dep
122///@{
123enum class Dep : unsigned {
124 None = 0,
125 Axiom = 1 << 0,
126 Infer = 1 << 1,
127 Mut = 1 << 2,
128 Proxy = 1 << 3,
129 Var = 1 << 4,
130};
131///@}
132
133} // namespace mim
134#ifndef DOXYGEN
135template<> struct fe::is_bit_enum<mim::Dep> : std::true_type {};
136#endif
137
138namespace mim {
139
140/// Use as mixin to wrap all kind of Def::proj and Def::projs variants.
141#define MIM_PROJ(NAME, CONST) \
142 nat_t num_##NAME##s() CONST noexcept { return ((const Def*)NAME())->num_projs(); } \
143 nat_t num_t##NAME##s() CONST noexcept { return ((const Def*)NAME())->num_tprojs(); } \
144 Ref NAME(nat_t a, nat_t i) CONST noexcept { return ((const Def*)NAME())->proj(a, i); } \
145 Ref NAME(nat_t i) CONST noexcept { return ((const Def*)NAME())->proj(i); } \
146 Ref t##NAME(nat_t i) CONST noexcept { return ((const Def*)NAME())->tproj(i); } \
147 template<nat_t A = std::dynamic_extent, class F> auto NAME##s(F f) CONST noexcept { \
148 return ((const Def*)NAME())->projs<A, F>(f); \
149 } \
150 template<class F> auto t##NAME##s(F f) CONST noexcept { return ((const Def*)NAME())->tprojs<F>(f); } \
151 template<nat_t A = std::dynamic_extent> auto NAME##s() CONST noexcept { return ((const Def*)NAME())->projs<A>(); } \
152 auto t##NAME##s() CONST noexcept { return ((const Def*)NAME())->tprojs(); } \
153 template<class F> auto NAME##s(nat_t a, F f) CONST noexcept { return ((const Def*)NAME())->projs<F>(a, f); } \
154 auto NAME##s(nat_t a) CONST noexcept { return ((const Def*)NAME())->projs(a); }
155
156/// CRTP-based Mixin to declare setters for Def::loc \& Def::name using a *covariant* return type.
157template<class P, class D = Def> class Setters { // D is only needed to make the resolution `D::template set` lazy
158private:
159 P* super() { return static_cast<P*>(this); }
160 const P* super() const { return static_cast<const P*>(this); }
161
162public:
163 // clang-format off
164 template<bool Ow = false> const P* set(Loc l ) const { super()->D::template set<Ow>(l); return super(); }
165 template<bool Ow = false> P* set(Loc l ) { super()->D::template set<Ow>(l); return super(); }
166 template<bool Ow = false> const P* set( Sym s ) const { super()->D::template set<Ow>(s); return super(); }
167 template<bool Ow = false> P* set( Sym s ) { super()->D::template set<Ow>(s); return super(); }
168 template<bool Ow = false> const P* set( std::string s) const { super()->D::template set<Ow>(std::move(s)); return super(); }
169 template<bool Ow = false> P* set( std::string s) { super()->D::template set<Ow>(std::move(s)); return super(); }
170 template<bool Ow = false> const P* set(Loc l, Sym s ) const { super()->D::template set<Ow>(l, s); return super(); }
171 template<bool Ow = false> P* set(Loc l, Sym s ) { super()->D::template set<Ow>(l, s); return super(); }
172 template<bool Ow = false> const P* set(Loc l, std::string s) const { super()->D::template set<Ow>(l, std::move(s)); return super(); }
173 template<bool Ow = false> P* set(Loc l, std::string s) { super()->D::template set<Ow>(l, std::move(s)); return super(); }
174 template<bool Ow = false> const P* set(Dbg d ) const { super()->D::template set<Ow>(d); return super(); }
175 template<bool Ow = false> P* set(Dbg d ) { super()->D::template set<Ow>(d); return super(); }
176 // clang-format on
177};
178
179/// Base class for all Def%s.
180///
181/// These are the most important subclasses:
182/// | Type Formation | Term Introduction | Term Elimination |
183/// | ----------------- | ----------------- | ----------------- |
184/// | Pi | Lam | App |
185/// | Sigma / Arr | Tuple / Pack | Extract |
186/// | | Insert | Insert |
187/// | Uniq | Wrap | Unwrap |
188/// | Join | Vel | Test |
189/// | Meet | Ac | Pick |
190/// | Nat | Lit | |
191/// | Idx | Lit | |
192/// In addition there is:
193/// * Var: A variable. Currently the following Def%s may be binders:
194/// * Pi, Lam, Sigma, Arr, Pack
195/// * Axiom: To introduce new entities.
196/// * Proxy: Used for intermediate values during optimizations.
197/// * Infer: Hole in the presentation filled by type inference (always mutable as the holes are filled in later).
198/// * Type, Univ, UMax, UInc: To keep track of type levels.
199///
200/// The data layout (see World::alloc and Def::deps) looks like this:
201/// ```
202/// Def| type | op(0) ... op(num_ops-1) |
203/// |-----------ops-----------|
204/// deps
205/// |--------------------------------| if type() != nullptr && is_set()
206/// |-------------------------| if type() == nullptr && is_set()
207/// |------| if type() != nullptr && !is_set()
208/// || if type() == nullptr && !is_set()
209/// ```
210/// @attention This means that any subclass of Def **must not** introduce additional members.
211/// @see @ref mut
212class Def : public fe::RuntimeCast<Def> {
213private:
214 Def& operator=(const Def&) = delete;
215 Def(const Def&) = delete;
216
217protected:
218 /// @name C'tors and D'tors
219 ///@{
220 Def(World*, node_t, const Def* type, Defs ops, flags_t flags); ///< Constructor for an *immutable* Def.
221 Def(node_t n, const Def* type, Defs ops, flags_t flags); ///< As above but World retrieved from @p type.
222 Def(node_t, const Def* type, size_t num_ops, flags_t flags); ///< Constructor for a *mutable* Def.
223 virtual ~Def() = default;
224 ///@}
225
226public:
227 /// @name Getters
228 ///@{
229 World& world() const noexcept;
230 constexpr flags_t flags() const noexcept { return flags_; }
231 constexpr u32 gid() const noexcept { return gid_; }
232 constexpr size_t hash() const noexcept { return hash_; }
233 constexpr node_t node() const noexcept { return node_; }
234 std::string_view node_name() const;
235 ///@}
236
237 /// @name type
238 ///@{
239
240 /// Yields the **raw** type of this Def, i.e. maybe `nullptr`. @see Def::unfold_type.
241 Ref type() const noexcept { return type_; }
242 /// Yields the type of this Def and builds a new `Type (UInc n)` if necessary.
243 Ref unfold_type() const;
244 /// Yields `true` if `this:T` and `T:(Type 0)`.
245 bool is_term() const;
246 ///@}
247
248 /// @name arity
249 ///@{
250 Ref arity() const;
251 std::optional<nat_t> isa_lit_arity() const;
253 auto a = isa_lit_arity();
254 assert(a.has_value());
255 return *a;
256 }
257 ///@}
258
259 /// @name ops
260 ///@{
261 template<size_t N = std::dynamic_extent> constexpr auto ops() const noexcept {
262 return View<const Def*, N>(ops_ptr(), num_ops_);
263 }
264 Ref op(size_t i) const noexcept { return ops()[i]; }
265 constexpr size_t num_ops() const noexcept { return num_ops_; }
266 ///@}
267
268 /// @name Setting Ops (Mutables Only)
269 /// @anchor set_ops
270 /// You can set and change the Def::ops of a mutable after construction.
271 /// However, you have to obey the following rules:
272 /// 1. If Def::is_set() is ...
273 /// 1. ... `false`, [set](@ref Def::set) the [operands](@ref Def::ops) from
274 /// * left (`i == 0`) to
275 /// * right (`i == num_ops() - 1`).
276 /// 2. ... `true`, [reset](@ref Def::reset) the operands from left to right as in 1a.
277 /// 2. In addition, you can invoke Def::unset() at *any time* to start over with 1a:
278 /// ```
279 /// mut->unset()->set({a, b, c}); // This will always work, but should be your last resort.
280 /// ```
281 ///
282 /// MimIR assumes that a mutable is *final*, when its last operand is set.
283 /// Then, Def::check() will be invoked.
284 ///@{
285 Def* set(size_t i, Ref); ///< Successively set from left to right.
286 Def* reset(size_t i, Ref def) { return unset(i)->set(i, def); } ///< Successively reset from left to right.
287 Def* set(Defs ops); ///< Def::set @p ops all at once.
288 Def* reset(Defs ops); ///< Def::reset @p ops all at once.
289 Def* unset(); ///< Unsets all Def::ops; works even, if not set at all or partially.
290 bool is_set() const; ///< Yields `true` if empty or the last op is set.
291 ///@}
292
293 /// @name deps
294 /// All *dependencies* of a Def and includes:
295 /// * Def::type() (if not `nullptr`) and
296 /// * the other Def::ops() (only included, if Def::is_set()) in this order.
297 ///@{
298 Defs deps() const;
299 Ref dep(size_t i) const { return deps()[i]; }
300 size_t num_deps() const { return deps().size(); }
301 ///@}
302
303 /// @name has_dep
304 /// Checks whether one Def::deps() contains specific elements defined in Dep.
305 /// This works up to the next *mutable*.
306 /// For example, consider the Tuple `tup`: `(<infer>, lam (x: Nat) = y)`:
307 /// ```
308 /// bool has_infer = tup->has_dep(Dep::Infer); // true;
309 /// bool has_mut = tup->has_dep(Dep::Mut); // true;
310 /// bool has_var = tup->has_dep(Dep::Var); // false - y is contained in another mutable;
311 /// ```
312 ///@{
313 bool has_dep() const { return dep_ != 0; }
314 bool has_dep(Dep d) const { return has_dep(unsigned(d)); }
315 bool has_dep(unsigned u) const { return dep_ & u; }
316 /// Neither a Dep::Mut nor a Dep::Var; can often be used as shortcut as an optimization.
317 bool has_const_dep() const { return !has_dep(Dep::Mut | Dep::Var); }
318 ///@}
319
320 /// @name proj
321 /// @anchor proj
322 /// Splits this Def via Extract%s or directly accessing the Def::ops in the case of Sigma%s or Arr%ays.
323 /// ```
324 /// std::array<const Def*, 2> ab = def->projs<2>();
325 /// std::array<u64, 2> xy = def->projs<2>([](auto def) { return Lit::as(def); });
326 /// auto [a, b] = def->projs<2>();
327 /// auto [x, y] = def->projs<2>([](auto def) { return Lit::as(def); });
328 /// Vector<const Def*> projs1 = def->projs(); // "projs1" has def->num_projs() many elements
329 /// Vector<const Def*> projs2 = def->projs(n);// "projs2" has n elements - asserts if incorrect
330 /// // same as above but applies Lit::as<nat_t>(def) to each element
331 /// Vector<const Lit*> lits1 = def->projs( [](auto def) { return Lit::as(def); });
332 /// Vector<const Lit*> lits2 = def->projs(n, [](auto def) { return Lit::as(def); });
333 /// ```
334 ///@{
335
336 /// Yields Def::as_lit_arity(), if it is in fact a Lit, or `1` otherwise.
337 nat_t num_projs() const { return isa_lit_arity().value_or(1); }
338 nat_t num_tprojs() const; ///< As above but yields 1, if Flags::scalarize_threshold is exceeded.
339
340 /// Similar to World::extract while assuming an arity of @p a, but also works on Sigma%s and Arr%ays.
341 Ref proj(nat_t a, nat_t i) const;
342 Ref proj(nat_t i) const { return proj(num_projs(), i); } ///< As above but takes Def::num_projs as arity.
343 Ref tproj(nat_t i) const { return proj(num_tprojs(), i); } ///< As above but takes Def::num_tprojs.
344
345 /// Splits this Def via Def::proj%ections into an Array (if `A == std::dynamic_extent`) or `std::array` (otherwise).
346 /// Applies @p f to each element.
347 template<nat_t A = std::dynamic_extent, class F> auto projs(F f) const {
348 using R = std::decay_t<decltype(f(this))>;
349 if constexpr (A == std::dynamic_extent) {
350 return projs(num_projs(), f);
351 } else {
352 assert(A == as_lit_arity());
353 std::array<R, A> array;
354 for (nat_t i = 0; i != A; ++i) array[i] = f(proj(A, i));
355 return array;
356 }
357 }
358
359 template<class F> auto tprojs(F f) const { return projs(num_tprojs(), f); }
360
361 template<class F> auto projs(nat_t a, F f) const {
362 using R = std::decay_t<decltype(f(this))>;
363 return Vector<R>(a, [&](nat_t i) { return f(proj(a, i)); });
364 }
365 template<nat_t A = std::dynamic_extent> auto projs() const {
366 return projs<A>([](Ref def) { return *def; });
367 }
368 auto tprojs() const {
369 return tprojs([](Ref def) { return *def; });
370 }
371 auto projs(nat_t a) const {
372 return projs(a, [](Ref def) { return *def; });
373 }
374 ///@}
375
376 /// @name var
377 /// @anchor var
378 /// Retrieve Var for *mutables*.
379 /// @see @ref proj
380 ///@{
382 /// Not necessarily a Var: E.g., if the return type is `[]`, this will yield `()`.
383 Ref var();
384 /// Only returns not `nullptr`, if Var of this mutable has ever been created.
385 const Var* has_var() { return var_; }
386 /// As above if `this` is a *mutable*.
387 const Var* has_var() const {
388 if (auto mut = isa_mut()) return mut->has_var();
389 return nullptr;
390 }
391 ///@}
392
393 /// @name Free Vars and Muts
394 /// * local_muts() / local_vars() are cached and hash-consed.
395 /// * free_vars() are computed on demand and cached in mutables.
396 /// They will be transitively invalidated by following users(), if a mutable is mutated.
397 ///@{
398
399 /// Mutables reachable by following *immutable* deps(); `mut->local_muts()` is by definition the set `{ mut }`.
400 Muts local_muts() const;
401 /// All local_muts() of this mutable's deps().
403 /// Var%s reachable by following *immutable* deps().
404 /// @note `var->local_vars()` is by definition the set `{ var }`.
405 Vars local_vars() const { return mut_ ? Vars() : vars_; }
406 /// Compute a global solution, i.e., by transitively following *mutables* as well.
407 Vars free_vars() const;
408 Vars free_vars();
409 Muts users() { return muts_; } ///< Set of mutables where this mutable is locally referenced.
410 bool is_open() const; ///< Has free_vars()?
411 bool is_closed() const; ///< Has no free_vars()?
412 ///@}
413
414 /// @name external
415 ///@{
416 bool is_external() const { return external_; }
417 void make_external();
418 void make_internal();
420 ///@}
421
422 /// @name Casts
423 /// @see @ref cast_builtin
424 ///@{
425 // clang-format off
426 template<class T = Def> const T* isa_imm() const { return isa_mut<T, true>(); }
427 template<class T = Def> const T* as_imm() const { return as_mut<T, true>(); }
428 template<class T = Def, class R> const T* isa_imm(R (T::*f)() const) const { return isa_mut<T, R, true>(f); }
429 // clang-format on
430
431 /// If `this` is *mut*able, it will cast `const`ness away and perform a `dynamic_cast` to @p T.
432 template<class T = Def, bool invert = false> T* isa_mut() const {
433 if constexpr (std::is_same<T, Def>::value)
434 return mut_ ^ invert ? const_cast<Def*>(this) : nullptr;
435 else
436 return mut_ ^ invert ? const_cast<Def*>(this)->template isa<T>() : nullptr;
437 }
438
439 /// Asserts that `this` is a *mutable*, casts `const`ness away and performs a `static_cast` to @p T.
440 template<class T = Def, bool invert = false> T* as_mut() const {
441 assert(mut_ ^ invert);
442 if constexpr (std::is_same<T, Def>::value)
443 return const_cast<Def*>(this);
444 else
445 return const_cast<Def*>(this)->template as<T>();
446 }
447 ///@}
448
449 /// @name Dbg Getters
450 ///@{
451 Dbg dbg() const { return dbg_; }
452 Loc loc() const { return dbg_.loc(); }
453 Sym sym() const { return dbg_.sym(); }
454 std::string unique_name() const; ///< name + "_" + Def::gid
455 ///@}
456
457 /// @name Dbg Setters
458 /// Every subclass `S` of Def has the same setters that return `S*`/`const S*` via the mixin Setters.
459 ///@{
460 // clang-format off
461 template<bool Ow = false> const Def* set(Loc l) const { if (Ow || !dbg_.loc()) dbg_.set(l); return this; }
462 template<bool Ow = false> Def* set(Loc l) { if (Ow || !dbg_.loc()) dbg_.set(l); return this; }
463 template<bool Ow = false> const Def* set(Sym s) const { if (Ow || !dbg_.sym()) dbg_.set(s); return this; }
464 template<bool Ow = false> Def* set(Sym s) { if (Ow || !dbg_.sym()) dbg_.set(s); return this; }
465 template<bool Ow = false> const Def* set( std::string s) const { set(sym(std::move(s))); return this; }
466 template<bool Ow = false> Def* set( std::string s) { set(sym(std::move(s))); return this; }
467 template<bool Ow = false> const Def* set(Loc l, Sym s ) const { set(l); set(s); return this; }
468 template<bool Ow = false> Def* set(Loc l, Sym s ) { set(l); set(s); return this; }
469 template<bool Ow = false> const Def* set(Loc l, std::string s) const { set(l); set(sym(std::move(s))); return this; }
470 template<bool Ow = false> Def* set(Loc l, std::string s) { set(l); set(sym(std::move(s))); return this; }
471 template<bool Ow = false> const Def* set(Dbg d) const { set(d.loc(), d.sym()); return this; }
472 template<bool Ow = false> Def* set(Dbg d) { set(d.loc(), d.sym()); return this; }
473 // clang-format on
474 ///@}
475
476 /// @name debug_prefix/suffix
477 /// Prepends/Appends a prefix/suffix to Def::name - but only in `Debug` build.
478 ///@{
479#ifndef NDEBUG
480 const Def* debug_prefix(std::string) const;
481 const Def* debug_suffix(std::string) const;
482#else
483 const Def* debug_prefix(std::string) const { return this; }
484 const Def* debug_suffix(std::string) const { return this; }
485#endif
486 ///@}
487
488 /// @name Rebuild
489 ///@{
490 Def* stub(World& w, Ref type) { return stub_(w, type)->set(dbg()); }
491 Def* stub(Ref type) { return stub(world(), type); }
492
493 /// Def::rebuild%s this Def while using @p new_op as substitute for its @p i'th Def::op
495 assert(isa_imm());
496 return rebuild_(w, type, ops)->set(dbg());
497 }
498 Ref rebuild(Ref type, Defs ops) const { return rebuild(world(), type, ops); }
499
500 /// Tries to make an immutable from a mutable.
501 /// This usually works if the mutable isn't recursive and its var isn't used.
502 virtual const Def* immutabilize() { return nullptr; }
503 bool is_immutabilizable();
504
505 Ref refine(size_t i, Ref new_op) const;
506
507 /// Rewrites Def::ops by substituting `this` mutable's Var with @p arg.
508 DefVec reduce(Ref arg) const;
509 DefVec reduce(Ref arg);
510 /// As above but only rewrites `this->op(i)`.
511 Ref reduce(size_t i, Ref arg) const;
512 ///@}
513
514 /// @name Type Checking
515 ///@{
516 virtual Ref check(size_t, Ref def) { return def; }
517 virtual Ref check() { return type(); }
518 const Def* zonk() const;
519 ///@}
520
521 /// @name dump
522 ///@{
523 void dump() const;
524 void dump(int max) const;
525 void write(int max) const;
526 void write(int max, const char* file) const;
527 std::ostream& stream(std::ostream&, int max) const;
528 ///@}
529
530 /// @name dot
531 /// Dumps DOT to @p os while obeying maximum recursion depth of @p max.
532 /// If @p types is `true`, Def::type() dependencies will be followed as well.
533 ///@{
534 void dot(std::ostream& os, uint32_t max = 0xFFFFFF, bool types = false) const;
535 /// Same as above but write to @p file or `std::cout` if @p file is `nullptr`.
536 void dot(const char* file = nullptr, uint32_t max = 0xFFFFFF, bool types = false) const;
537 void dot(const std::string& file, uint32_t max = 0xFFFFFF, bool types = false) const {
538 return dot(file.c_str(), max, types);
539 }
540 ///@}
541
542protected:
543 /// @name Wrappers for World::sym
544 /// These are here to have Def::set%ters inline without including `mim/world.h`.
545 ///@{
546 Sym sym(const char*) const;
547 Sym sym(std::string_view) const;
548 Sym sym(std::string) const;
549 ///@}
550
551private:
552 virtual Def* stub_(World&, Ref) { fe::unreachable(); }
553 virtual Ref rebuild_(World& w, Ref type, Defs ops) const = 0;
554
555 Vars free_vars(bool&, bool&, uint32_t run);
556 void invalidate();
557 Def* unset(size_t i);
558 const Def** ops_ptr() const {
559 return reinterpret_cast<const Def**>(reinterpret_cast<char*>(const_cast<Def*>(this + 1)));
560 }
561 bool equal(const Def* other) const;
562
563protected:
564 mutable Dbg dbg_;
565 union {
566 NormalizeFn normalizer_; ///< Axiom only: Axiom%s use this member to store their normalizer.
567 const Axiom* axiom_; ///< App only: Curried App%s of Axiom%s use this member to propagate the Axiom.
568 const Var* var_; ///< Mutable only: Var of a mutable.
569 mutable World* world_;
570 };
574
575private:
576 uint8_t node_;
577 bool mut_ : 1;
578 bool external_ : 1;
579 unsigned dep_ : 6;
580 uint32_t mark_ = 0;
581#ifndef NDEBUG
582 size_t curr_op_ = 0;
583#endif
584 u32 gid_;
585 u32 num_ops_;
586 size_t hash_;
587 Vars vars_; // Mutable: local vars; Immutable: free vars.
588 Muts muts_; // Mutable: local muts; Immutable: users.
589
590 const Def* type_;
591
592 friend class World;
593 friend void swap(World&, World&) noexcept;
594 friend std::ostream& operator<<(std::ostream&, const Def*);
595};
596
597/// @name DefDef
598///@{
599using DefDef = std::tuple<const Def*, const Def*>;
600
602 size_t operator()(DefDef pair) const {
603 if constexpr (sizeof(size_t) == 8)
604 return hash((u64(std::get<0>(pair)->gid()) << 32_u64) | u64(std::get<1>(pair)->gid()));
605 else
606 return hash_combine(hash_begin(std::get<0>(pair)->gid()), std::get<1>(pair)->gid());
607 }
608};
609
610struct DefDefEq {
611 bool operator()(DefDef p1, DefDef p2) const { return p1 == p2; }
612};
613
614template<class To> using DefDefMap = absl::flat_hash_map<DefDef, To, DefDefHash, DefDefEq>;
615using DefDefSet = absl::flat_hash_set<DefDef, DefDefHash, DefDefEq>;
616///@}
617
618class Var : public Def, public Setters<Var> {
619private:
620 Var(const Def* type, Def* mut)
621 : Def(Node, type, Defs{mut}, 0) {}
622
623public:
624 using Setters<Var>::set;
625
626 /// @name ops
627 ///@{
628 Def* mut() const { return op(0)->as_mut(); }
629 ///@}
630
631 static constexpr auto Node = Node::Var;
632
633private:
634 Ref rebuild_(World&, Ref, Defs) const override;
635
636 friend class World;
637};
638
639class Univ : public Def, public Setters<Univ> {
640public:
641 using Setters<Univ>::set;
642 static constexpr auto Node = Node::Univ;
643
644private:
646 : Def(&world, Node, nullptr, Defs{}, 0) {}
647
648 Ref rebuild_(World&, Ref, Defs) const override;
649
650 friend class World;
651};
652
653class UMax : public Def, public Setters<UMax> {
654public:
655 using Setters<UMax>::set;
656 static constexpr auto Node = Node::UMax;
657
658private:
659 UMax(World&, Defs ops);
660
661 Ref rebuild_(World&, Ref, Defs) const override;
662
663 friend class World;
664};
665
666class UInc : public Def, public Setters<UInc> {
667private:
668 UInc(const Def* op, level_t offset)
669 : Def(Node, op->type()->as<Univ>(), {op}, offset) {}
670
671public:
672 using Setters<UInc>::set;
673
674 /// @name ops
675 ///@{
676 Ref op() const { return Def::op(0); }
677 level_t offset() const { return flags(); }
678 ///@}
679
680 static constexpr auto Node = Node::UInc;
681
682private:
683 Ref rebuild_(World&, Ref, Defs) const override;
684
685 friend class World;
686};
687
688class Type : public Def, public Setters<Type> {
689private:
690 Type(const Def* level)
691 : Def(Node, nullptr, {level}, 0) {}
692
693public:
694 using Setters<Type>::set;
695
696 /// @name ops
697 ///@{
698 Ref level() const { return op(0); }
699 ///@}
700
701 static constexpr auto Node = Node::Type;
702
703private:
704 Ref rebuild_(World&, Ref, Defs) const override;
705
706 friend class World;
707};
708
709class Lit : public Def, public Setters<Lit> {
710private:
711 Lit(const Def* type, flags_t val)
712 : Def(Node, type, Defs{}, val) {}
713
714public:
715 using Setters<Lit>::set;
716
717 /// @name Get actual Constant
718 ///@{
719 template<class T = flags_t> T get() const {
720 static_assert(sizeof(T) <= 8);
721 return bitcast<T>(flags_);
722 }
723 ///@}
724
725 using Def::as;
726 using Def::isa;
727
728 /// @name Casts
729 ///@{
730 /// @see @ref cast_lit
731 template<class T = nat_t> static std::optional<T> isa(Ref def) {
732 if (!def) return {};
733 if (auto lit = def->isa<Lit>()) return lit->get<T>();
734 return {};
735 }
736 template<class T = nat_t> static T as(Ref def) { return def->as<Lit>()->get<T>(); }
737 ///@}
738
739 static constexpr auto Node = Node::Lit;
740
741private:
742 Ref rebuild_(World&, Ref, Defs) const override;
743
744 friend class World;
745};
746
747class Nat : public Def, public Setters<Nat> {
748public:
749 using Setters<Nat>::set;
750 static constexpr auto Node = Node::Nat;
751
752private:
753 Nat(World& world);
754
755 Ref rebuild_(World&, Ref, Defs) const override;
756
757 friend class World;
758};
759
760/// A built-in constant of type `Nat -> *`.
761class Idx : public Def, public Setters<Idx> {
762private:
763 Idx(const Def* type)
764 : Def(Node, type, Defs{}, 0) {}
765
766public:
767 using Setters<Idx>::set;
768 using Def::as;
769 using Def::isa;
770
771 /// @name isa
772 ///@{
773
774 /// Checks if @p def is a `Idx s` and returns `s` or `nullptr` otherwise.
775 static Ref isa(Ref def);
776 static Ref as(Ref def) {
777 auto res = isa(def);
778 assert(res);
779 return res;
780 }
781 static std::optional<nat_t> isa_lit(Ref def);
782 static nat_t as_lit(Ref def) {
783 auto res = isa_lit(def);
784 assert(res.has_value());
785 return *res;
786 }
787 ///@}
788
789 /// @name Convert between Idx::isa and bitwidth and vice versa
790 ///@{
791 // clang-format off
792 static constexpr nat_t bitwidth2size(nat_t n) { assert(n != 0); return n == 64 ? 0 : (1_n << n); }
793 static constexpr nat_t size2bitwidth(nat_t n) { return n == 0 ? 64 : std::bit_width(n - 1_n); }
794 // clang-format on
795 static std::optional<nat_t> size2bitwidth(Ref size);
796 ///@}
797
798 static constexpr auto Node = Node::Idx;
799
800private:
801 Ref rebuild_(World&, Ref, Defs) const override;
802
803 friend class World;
804};
805
806class Proxy : public Def, public Setters<Proxy> {
807private:
808 Proxy(const Def* type, Defs ops, u32 pass, u32 tag)
809 : Def(Node, type, ops, (u64(pass) << 32_u64) | u64(tag)) {}
810
811public:
812 using Setters<Proxy>::set;
813
814 /// @name Getters
815 ///@{
816 u32 pass() const { return u32(flags() >> 32_u64); } ///< IPass::index within PassMan.
817 u32 tag() const { return u32(flags()); }
818 ///@}
819
820 static constexpr auto Node = Node::Proxy;
821
822private:
823 Ref rebuild_(World&, Ref, Defs) const override;
824
825 friend class World;
826};
827
828/// @deprecated A global variable in the data segment.
829/// A Global may be mutable or immutable.
830/// @deprecated Will be removed.
831class Global : public Def, public Setters<Global> {
832private:
833 Global(const Def* type, bool is_mutable)
834 : Def(Node, type, 1, is_mutable) {}
835
836public:
837 using Setters<Global>::set;
838
839 /// @name ops
840 ///@{
841 Ref init() const { return op(0); }
842 void set(Ref init) { Def::set(0, init); }
843 ///@}
844
845 /// @name type
846 ///@{
847 const App* type() const;
848 Ref alloced_type() const;
849 ///@}
850
851 /// @name Getters
852 ///@{
853 bool is_mutable() const { return flags(); }
854 ///@}
855
856 /// @name Rebuild
857 ///@{
858 Global* stub(Ref type) { return stub_(world(), type)->set(dbg()); }
859 ///@}
860
861 static constexpr auto Node = Node::Global;
862
863private:
864 Ref rebuild_(World&, Ref, Defs) const override;
865 Global* stub_(World&, Ref) override;
866
867 friend class World;
868};
869
870} // namespace mim
Base class for all Defs.
Definition def.h:212
bool is_set() const
Yields true if empty or the last op is set.
Definition def.cpp:281
Defs deps() const
Definition def.cpp:410
Ref op(size_t i) const noexcept
Definition def.h:264
const Def * set(Dbg d) const
Definition def.h:471
void dot(std::ostream &os, uint32_t max=0xFFFFFF, bool types=false) const
Definition dot.cpp:126
void make_internal()
Definition def.cpp:478
T * as_mut() const
Asserts that this is a mutable, casts constness away and performs a static_cast to T.
Definition def.h:440
const Var * has_var() const
As above if this is a mutable.
Definition def.h:387
void dump() const
Definition dump.cpp:449
Ref var()
Not necessarily a Var: E.g., if the return type is [], this will yield ().
Definition def.cpp:423
auto projs() const
Definition def.h:365
const T * isa_imm(R(T::*f)() const) const
Definition def.h:428
u8 trip_
Definition def.h:573
bool has_dep(Dep d) const
Definition def.h:314
nat_t num_tprojs() const
As above but yields 1, if Flags::scalarize_threshold is exceeded.
Definition def.cpp:482
virtual ~Def()=default
const Def * zonk() const
Definition check.cpp:23
nat_t as_lit_arity() const
Definition def.h:252
bool has_dep(unsigned u) const
Definition def.h:315
World & world() const noexcept
Definition def.cpp:384
Muts mut_local_muts()
All local_muts() of this mutable's deps().
Definition def.cpp:298
void dot(const std::string &file, uint32_t max=0xFFFFFF, bool types=false) const
Definition def.h:537
std::string_view node_name() const
Definition def.cpp:400
Ref dep(size_t i) const
Definition def.h:299
auto projs(nat_t a, F f) const
Definition def.h:361
Ref tproj(nat_t i) const
As above but takes Def::num_tprojs.
Definition def.h:343
constexpr auto ops() const noexcept
Definition def.h:261
Vars local_vars() const
Vars reachable by following immutable deps().
Definition def.h:405
void make_external()
Definition def.cpp:477
Ref unfold_type() const
Yields the type of this Def and builds a new Type (UInc n) if necessary.
Definition def.cpp:390
constexpr flags_t flags() const noexcept
Definition def.h:230
virtual Ref check()
Definition def.h:517
Def * set(size_t i, Ref)
Successively set from left to right.
Definition def.cpp:243
Dbg dbg_
Definition def.h:564
const Def * set(std::string s) const
Definition def.h:465
T * isa_mut() const
If this is *mut*able, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:432
Vars free_vars()
Definition def.cpp:312
DefVec reduce(Ref arg) const
Rewrites Def::ops by substituting this mutable's Var with arg.
Definition def.cpp:204
Def * stub(World &w, Ref type)
Definition def.h:490
auto projs(nat_t a) const
Definition def.h:371
u8 curry_
Definition def.h:572
auto tprojs() const
Definition def.h:368
friend std::ostream & operator<<(std::ostream &, const Def *)
This will stream def as an operand.
Definition dump.cpp:421
bool is_term() const
Yields true if this:T and T:(Type 0).
Definition def.cpp:439
bool has_const_dep() const
Neither a Dep::Mut nor a Dep::Var; can often be used as shortcut as an optimization.
Definition def.h:317
const Def * debug_prefix(std::string) const
Definition def.cpp:417
bool is_immutabilizable()
Definition def.cpp:160
bool is_external() const
Definition def.h:416
void transfer_external(Def *to)
Definition def.h:419
Def * reset(size_t i, Ref def)
Successively reset from left to right.
Definition def.h:286
Def * stub(Ref type)
Definition def.h:491
auto projs(F f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
Definition def.h:347
virtual Def * stub_(World &, Ref)
Definition def.h:552
bool is_open() const
Has free_vars()?
Definition def.cpp:371
constexpr size_t hash() const noexcept
Definition def.h:232
virtual const Def * immutabilize()
Tries to make an immutable from a mutable.
Definition def.h:502
Ref rebuild(Ref type, Defs ops) const
Definition def.h:498
Def * set(Dbg d)
Definition def.h:472
Muts local_muts() const
Mutables reachable by following immutable deps(); mut->local_muts() is by definition the set { mut }...
Definition def.cpp:293
Ref type() const noexcept
Yields the raw type of this Def, i.e. maybe nullptr.
Definition def.h:241
Def * set(Sym s)
Definition def.h:464
const Def * debug_suffix(std::string) const
Definition def.cpp:418
const Def * set(Sym s) const
Definition def.h:463
Ref proj(nat_t i) const
As above but takes Def::num_projs as arity.
Definition def.h:342
Def * set(Loc l)
Definition def.h:462
const Def * set(Loc l, Sym s) const
Definition def.h:467
virtual Ref check(size_t, Ref def)
Definition def.h:516
Def * set(std::string s)
Definition def.h:466
bool has_dep() const
Definition def.h:313
const Def * set(Loc l) const
Definition def.h:461
Loc loc() const
Definition def.h:452
void write(int max) const
Definition dump.cpp:457
auto tprojs(F f) const
Definition def.h:359
constexpr node_t node() const noexcept
Definition def.h:233
Def * set(Loc l, std::string s)
Definition def.h:470
virtual Ref rebuild_(World &w, Ref type, Defs ops) const =0
const T * as_imm() const
Definition def.h:427
Ref arity() const
Definition def.cpp:450
Sym sym() const
Definition def.h:453
nat_t num_projs() const
Yields Def::as_lit_arity(), if it is in fact a Lit, or 1 otherwise.
Definition def.h:337
std::optional< nat_t > isa_lit_arity() const
Definition def.cpp:457
std::ostream & stream(std::ostream &, int max) const
Definition dump.cpp:430
flags_t flags_
Definition def.h:571
friend void swap(World &, World &) noexcept
Ref rebuild(World &w, Ref type, Defs ops) const
Def::rebuilds this Def while using new_op as substitute for its i'th Def::op.
Definition def.h:494
Ref 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.
Definition def.cpp:487
constexpr u32 gid() const noexcept
Definition def.h:231
Def * unset()
Unsets all Def::ops; works even, if not set at all or partially.
Definition def.cpp:259
std::string unique_name() const
name + "_" + Def::gid
Definition def.cpp:480
const Def * set(Loc l, std::string s) const
Definition def.h:469
const T * isa_imm() const
Definition def.h:426
Muts users()
Set of mutables where this mutable is locally referenced.
Definition def.h:409
Ref refine(size_t i, Ref new_op) const
Definition def.cpp:228
bool is_closed() const
Has no free_vars()?
Definition def.cpp:363
Def * set(Loc l, Sym s)
Definition def.h:468
Dbg dbg() const
Definition def.h:451
const Var * has_var()
Only returns not nullptr, if Var of this mutable has ever been created.
Definition def.h:385
size_t num_deps() const
Definition def.h:300
constexpr size_t num_ops() const noexcept
Definition def.h:265
Ref alloced_type() const
Definition def.cpp:539
Ref rebuild_(World &, Ref, Defs) const override
Definition def.cpp:90
Ref init() const
Definition def.h:841
bool is_mutable() const
Definition def.h:853
const App * type() const
Definition def.cpp:538
Global * stub_(World &, Ref) override
Definition def.cpp:129
Global * stub(Ref type)
Definition def.h:858
static constexpr auto Node
Definition def.h:861
void set(Ref init)
Definition def.h:842
A built-in constant of type Nat -> *.
Definition def.h:761
static Ref as(Ref def)
Definition def.h:776
static constexpr auto Node
Definition def.h:798
static constexpr nat_t size2bitwidth(nat_t n)
Definition def.h:793
static constexpr nat_t bitwidth2size(nat_t n)
Definition def.h:792
static Ref isa(Ref def)
Checks if def is a Idx s and returns s or nullptr otherwise.
Definition def.cpp:514
static nat_t as_lit(Ref def)
Definition def.h:782
Ref rebuild_(World &, Ref, Defs) const override
Definition def.cpp:91
static std::optional< nat_t > isa_lit(Ref def)
Definition def.cpp:522
This node is a hole in the IR that is inferred by its context later on.
Definition check.h:10
Ref rebuild_(World &, Ref, Defs) const override
Definition def.cpp:100
static std::optional< T > isa(Ref def)
Definition def.h:731
static constexpr auto Node
Definition def.h:739
T get() const
Definition def.h:719
static T as(Ref def)
Definition def.h:736
Ref rebuild_(World &, Ref, Defs) const override
Definition def.cpp:92
static constexpr auto Node
Definition def.h:750
Ref rebuild_(World &, Ref, Defs) const override
Definition def.cpp:104
static constexpr auto Node
Definition def.h:820
u32 pass() const
IPass::index within PassMan.
Definition def.h:816
u32 tag() const
Definition def.h:817
Helper class to retrieve Infer::arg if present.
Definition def.h:86
Ref(const Def *def)
Definition def.h:89
const Def * operator*() const
Definition def.h:92
const Def * def() const
Retrieve wrapped Def without Infer::refering.
Definition def.h:97
static const Def * refer(const Def *def)
Same as Infer::find but does nothing if def is nullptr.
Definition check.cpp:29
Ref()=default
const Def * operator->() const
Definition def.h:93
friend std::ostream & operator<<(std::ostream &, Ref)
Definition dump.cpp:417
CRTP-based Mixin to declare setters for Def::loc & Def::name using a covariant return type.
Definition def.h:157
P * set(Loc l, Sym s)
Definition def.h:171
P * set(std::string s)
Definition def.h:169
const P * set(Sym s) const
Definition def.h:166
const P * set(Loc l, Sym s) const
Definition def.h:170
const P * set(Loc l) const
Definition def.h:164
P * set(Loc l)
Definition def.h:165
const P * set(Dbg d) const
Definition def.h:174
const P * set(Loc l, std::string s) const
Definition def.h:172
P * set(Sym s)
Definition def.h:167
P * set(Dbg d)
Definition def.h:175
P * set(Loc l, std::string s)
Definition def.h:173
const P * set(std::string s) const
Definition def.h:168
This is a thin wrapper for std::span<T, N> with the following additional features:
Definition span.h:28
Ref level() const
Definition def.h:698
Ref rebuild_(World &, Ref, Defs) const override
Definition def.cpp:107
static constexpr auto Node
Definition def.h:701
Ref op() const
Definition def.h:676
static constexpr auto Node
Definition def.h:680
level_t offset() const
Definition def.h:677
Ref rebuild_(World &, Ref, Defs) const override
Definition def.cpp:110
static constexpr auto Node
Definition def.h:656
Ref rebuild_(World &, Ref, Defs) const override
Definition def.cpp:111
static constexpr auto Node
Definition def.h:642
Ref rebuild_(World &, Ref, Defs) const override
Definition def.cpp:93
static constexpr auto Node
Definition def.h:631
Ref rebuild_(World &, Ref, Defs) const override
Definition def.cpp:112
Def * mut() const
Definition def.h:628
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:33
#define MIM_NODE(m)
Definition def.h:18
#define MIM_PROJ(NAME, CONST)
Use as mixin to wrap all kind of Def::proj and Def::projs variants.
Definition def.h:141
@ Type
Definition def.h:40
@ Univ
Definition def.h:40
@ UMax
Definition def.h:40
@ Proxy
Definition def.h:40
@ Var
Definition def.h:40
@ Nat
Definition def.h:40
@ Idx
Definition def.h:40
@ Global
Definition def.h:40
@ UInc
Definition def.h:40
@ Lit
Definition def.h:40
constexpr auto Num_Nodes
Definition def.h:44
Definition ast.h:14
u64 nat_t
Definition types.h:43
DefMap< const Def * > Def2Def
Definition def.h:60
absl::flat_hash_map< DefDef, To, DefDefHash, DefDefEq > DefDefMap
Definition def.h:614
D bitcast(const S &src)
A bitcast from src of type S to D.
Definition util.h:23
Dep
Definition def.h:123
u64 flags_t
Definition types.h:45
u8 node_t
Definition types.h:44
Span< const T, N > View
Definition span.h:89
std::tuple< const Def *, const Def * > DefDef
Definition def.h:599
absl::flat_hash_set< K, GIDHash< K >, GIDEq< K > > GIDSet
Definition util.h:182
GIDSet< const Var * > VarSet
Definition def.h:78
GIDMap< const Var *, To > VarMap
Definition def.h:77
u64 level_t
Definition types.h:42
GIDMap< const Def *, To > DefMap
Definition def.h:58
size_t hash_combine(size_t seed, T v)
Definition hash.h:58
GIDSet< Def * > MutSet
Definition def.h:69
MutMap< Def * > Mut2Mut
Definition def.h:70
absl::flat_hash_map< K, V, GIDHash< K >, GIDEq< K > > GIDMap
Definition util.h:181
GIDSet< const Def * > DefSet
Definition def.h:59
size_t hash(size_t h)
Definition hash.h:32
PooledSet< const Var * > Vars
Definition def.h:80
uint32_t u32
Definition types.h:34
absl::flat_hash_set< DefDef, DefDefHash, DefDefEq > DefDefSet
Definition def.h:615
uint64_t u64
Definition types.h:34
size_t hash_begin()
Definition hash.h:71
uint8_t u8
Definition types.h:34
Ref(*)(Ref, Ref, Ref) NormalizeFn
Definition def.h:105
Sort
Definition def.h:110
Vector(I, I, A=A()) -> Vector< typename std::iterator_traits< I >::value_type, Default_Inlined_Size< typename std::iterator_traits< I >::value_type >, A >
GIDMap< Def *, To > MutMap
Definition def.h:68
VarMap< const Var * > Var2Var
Definition def.h:79
constexpr decltype(auto) get(mim::Span< T, N > span)
Definition span.h:113
Sym sym() const
Definition dbg.h:145
Loc loc() const
Definition dbg.h:146
Dbg & set(Sym sym)
Definition dbg.h:153
bool operator()(DefDef p1, DefDef p2) const
Definition def.h:611
size_t operator()(DefDef pair) const
Definition def.h:602
#define CODE(t, str)
Definition tok.h:54