Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vars #269

Merged
merged 58 commits into from
Apr 3, 2024
Merged

Vars #269

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
c005e8b
wip: keep track of free vars
leissa Mar 8, 2024
b6a3ab0
Merge branch 'dot' into vars
leissa Mar 13, 2024
2f6ffe1
wip: free_vars
leissa Mar 14, 2024
d36dad4
Merge branch 'dot' into vars
leissa Mar 14, 2024
8d15e92
Merge branch 'dot' into vars
leissa Mar 14, 2024
76f72cc
cache var in mut + bug fix in free_vars
leissa Mar 14, 2024
cb05498
cleanup + bug fixes
leissa Mar 14, 2024
7a2072f
wip: VarRewriter
leissa Mar 15, 2024
a15a0a3
docs
leissa Mar 15, 2024
40c2da1
bug fix for recursion in VarRewriter
leissa Mar 16, 2024
02ad9a8
wip: PooledSet
leissa Mar 16, 2024
bd5449c
use VLA instead of Def::ops_ptr()
leissa Mar 16, 2024
e67b2e1
Merge branch 'vars' into pooledset
leissa Mar 16, 2024
ad026b0
Revert "use VLA instead of Def::ops_ptr()"
leissa Mar 17, 2024
bb04605
FE bump
leissa Mar 17, 2024
46512aa
fix mem bug
leissa Mar 17, 2024
7c560b9
Merge branch 'dot' into pooledset
leissa Mar 17, 2024
0c1cbfc
Merge branch 'vars' into pooledset
leissa Mar 17, 2024
56d6247
Vars + Muts in new PooledSet
leissa Mar 18, 2024
196769f
VarRewriter uses Vas (PooledSet) now + docs
leissa Mar 18, 2024
a0c2d22
polish
leissa Mar 18, 2024
d70deb6
resolved TODO: use find_and_replace
leissa Mar 18, 2024
08dc1fb
bug fix in Tab: post inc/dec
leissa Mar 18, 2024
963402d
dot: proper esc
leissa Mar 18, 2024
22b1ecc
wip: debug code for lazy free_vars
leissa Mar 19, 2024
0c49c92
Scope: removed Freezer in favor of entry_->has_var()
leissa Mar 19, 2024
03e45f0
use fixpoint to calculate free_vars
leissa Mar 19, 2024
21f9cc3
Merge branch 'vars_fp' into vars_debug2
leissa Mar 19, 2024
8e1a3d8
cleanup
leissa Mar 19, 2024
e53cd31
cleanup
leissa Mar 19, 2024
bac7172
polish
leissa Mar 19, 2024
c8f4dbc
polish
leissa Mar 19, 2024
863996b
Merge branch 'master' into vars
leissa Mar 20, 2024
50bedd1
Merge branch 'maintenance' into vars
leissa Mar 20, 2024
008b085
merge with maintenance
leissa Mar 20, 2024
16c7662
Merge branch 'maintenance' into vars
leissa Mar 20, 2024
9f58978
Merge branch 'maintenance' into vars
leissa Mar 20, 2024
3c0949d
ScopeRewriter -> VarRewriter
leissa Mar 20, 2024
b5562b2
ScopeRewriter -> VarRewriter
leissa Mar 20, 2024
da1a2f6
polish
leissa Mar 20, 2024
8c43ec8
Scope -> VarRewriter
leissa Mar 20, 2024
e633b23
Merge branch 'master' into vars
leissa Mar 20, 2024
1769b3d
version bump
leissa Mar 20, 2024
ba96635
Scope + rewrite -> VarRewriter
leissa Mar 20, 2024
f7cf01b
verify that externals are Def::is_closed
leissa Mar 20, 2024
286a94d
lam_spec: use old_lam->is_closed() instead of weird is_top_level
leissa Mar 20, 2024
49d8c71
Removed ScopeRewriter in favor of new VarRewriter
leissa Mar 20, 2024
75b7c3a
polish
leissa Mar 20, 2024
58d31a5
polish
leissa Mar 20, 2024
6fae5cf
optimize with unions
leissa Mar 20, 2024
a62e6ae
polish
leissa Mar 20, 2024
a5d7254
docs
leissa Mar 20, 2024
948aed2
typos
leissa Mar 20, 2024
52cbd6f
docs
leissa Mar 20, 2024
b069d0d
Merge branch 'master' into vars
leissa Mar 20, 2024
7c19ca7
docs
leissa Mar 21, 2024
27e7038
ClosedMutPhase: new alternative for ScopePhase
leissa Mar 21, 2024
6d6155c
resolve comments for PR#269
leissa Apr 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/coding.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ What is more, you can adjust the output behavior directly from within GDB by mod
```gdb
(gdb) call world.flags().dump_gid = 1
(gdb) call world.flags().dump_recursive = 1
(gdb) call world.log().level = 4
(gdb) call world().log().max_level_ = 4
```
Another useful feature is to retrieve a `Def*` from a thorin::Def::gid via thorin::World::gid2def:
```gdb
Expand All @@ -112,7 +112,7 @@ Just source `scripts/xdot.gdb` in your `~/.gdbinit`:
source ~/thorin2/scripts/xdot.gdb
```
Here is the `xdot` GDB command in action:
![gdb-xdot](gdb-xdot.png)
![cgdb session using xdot](gdb-xdot.png)

\include "xdot-help.gdb"

Expand Down
14 changes: 14 additions & 0 deletions gtest/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,20 @@ TEST(Check, alpha) {
check(l_1, l_1, true, true);
}

TEST(FV, free_vars) {
Driver driver;
World& w = driver.world();
auto Nat = w.type_nat();
auto pi = w.pi(Nat, w.sigma({Nat, Nat}));
auto lx = w.mut_lam(pi);
auto ly = w.mut_lam(pi);
auto x = lx->var()->set("x");
auto y = ly->var()->set("y");
lx->set(false, w.tuple({x, y}));
auto fvs = lx->free_vars();
outln("{, }", fvs);
}

TEST(ADT, Span) {
{
int a[3] = {0, 1, 2};
Expand Down
3 changes: 2 additions & 1 deletion include/thorin/check.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ class Infer : public Def {
/// [Union by rank](https://en.wikipedia.org/wiki/Disjoint-set_data_structure#Union_by_rank).
static const Def* find(const Def*);

Infer* stub(World&, Ref) override;
Infer* stub(Ref type) { return stub_(world(), type)->set(dbg()); }

private:
Infer* stub_(World&, Ref) override;
flags_t rank() const { return flags(); }
flags_t& rank() { return flags_; }

Expand Down
104 changes: 85 additions & 19 deletions include/thorin/def.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "thorin/util/dbg.h"
#include "thorin/util/hash.h"
#include "thorin/util/pool.h"
#include "thorin/util/print.h"
#include "thorin/util/util.h"
#include "thorin/util/vector.h"
Expand Down Expand Up @@ -68,6 +69,7 @@ using DefVec = Vector<const Def*>;
template<class To> using MutMap = GIDMap<Def*, To>;
using MutSet = GIDSet<Def*>;
using Mut2Mut = MutMap<Def*>;
using Muts = PooledSet<Def*>;
///@}

/// @name Var
Expand All @@ -76,6 +78,7 @@ using Mut2Mut = MutMap<Def*>;
template<class To> using VarMap = GIDMap<const Var*, To>;
using VarSet = GIDSet<const Var*>;
using Var2Var = VarMap<const Var*>;
using Vars = PooledSet<const Var*>;
///@{

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -192,21 +195,27 @@ public:
# define THORIN_SETTERS(T) THORIN_SETTERS_(T)
#endif

#define THORIN_DEF_MIXIN(T) \
public: \
THORIN_SETTERS(T) \
Ref rebuild(World&, Ref, Defs) const override; \
static constexpr auto Node = Node::T; \
\
private: \
#define THORIN_DEF_MIXIN(T) \
public: \
THORIN_SETTERS(T) \
static constexpr auto Node = Node::T; \
\
private: \
Ref rebuild_(World&, Ref, Defs) const override; \
friend class World;

/// Base class for all Def%s.
/// The data layout (see World::alloc and Def::partial_ops) looks like this:
/// ```
/// Def| type | op(0) ... op(num_ops-1) |
/// |---------partial_ops------------|
/// |-------extended_ops------|
/// |-----------ops-----------|
/// |----------partial_ops-----------|
///
/// extended_ops
/// |--------------------------------| if type() != nullptr && is_set()
/// |-------------------------| if type() == nullptr && is_set()
/// |------| if type() != nullptr && !is_set()
/// || if type() == nullptr && !is_set()
/// ```
/// @attention This means that any subclass of Def **must not** introduce additional members.
/// @see @ref mut
Expand All @@ -217,8 +226,8 @@ class Def : public fe::RuntimeCast<Def> {

protected:
Def(World*, node_t, const Def* type, Defs ops, flags_t flags); ///< Constructor for an *immutable* Def.
Def(node_t n, const Def* type, Defs ops, flags_t flags);
Def(node_t, const Def* type, size_t num_ops, flags_t flags); ///< Constructor for a *mutable* Def.
Def(node_t n, const Def* type, Defs ops, flags_t flags); ///< As above but World retrieved from @p type.
Def(node_t, const Def* type, size_t num_ops, flags_t flags); ///< Constructor for a *mutable* Def.
virtual ~Def() = default;

public:
Expand Down Expand Up @@ -391,8 +400,32 @@ class Def : public fe::RuntimeCast<Def> {
///@{
/// Retrieve Var for *mutables*.
/// @see @ref proj
Ref var();
THORIN_PROJ(var, )
/// Not necessarily a Var: E.g., if the return type is `[]`, this will yield `()`.
Ref var();
/// Only returns not `nullptr`, if Var of this mutable has ever been created.
const Var* has_var() { return var_; }
/// As above if `this` is a *mutable*.
const Var* has_var() const {
if (auto mut = isa_mut()) return mut->has_var();
return nullptr;
}
///@}

/// @name Free Vars and Muts
///@{
/// * local_muts()/local_vars() are Var%s/mutables reachable by following *immutable* extended_ops().
/// * local_muts()/local_vars() are cached and hash-consed.
/// * free_vars() compute a global solution, i.e., by transitively following *mutables* as well.
/// * free_vars() are computed on demand and cached.
/// They will be transitively invalidated by following fv_consumers(), if a mutable is mutated.
Muts local_muts() const;
Vars local_vars() const { return mut_ ? Vars() : vars_.local; }
Vars free_vars() const;
Vars free_vars();
bool is_open() const; ///< Has free_vars()?
bool is_closed() const; ///< Has no free_vars()?
Muts fv_consumers() { return muts_.fv_consumers; }
///@}

/// @name external
Expand Down Expand Up @@ -458,10 +491,15 @@ class Def : public fe::RuntimeCast<Def> {

/// @name Rebuild
///@{
virtual Def* stub(World&, Ref) { fe::unreachable(); }
Def* stub(World& w, Ref type) { return stub_(w, type)->set(dbg()); }
Def* stub(Ref type) { return stub(world(), type); }

/// Def::rebuild%s this Def while using @p new_op as substitute for its @p i'th Def::op
virtual Ref rebuild(World& w, Ref type, Defs ops) const = 0;
Ref rebuild(World& w, Ref type, Defs ops) const {
assert(isa_imm());
return rebuild_(w, type, ops)->set(dbg());
}
Ref rebuild(Ref type, Defs ops) const { return rebuild(world(), type, ops); }

/// Tries to make an immutable from a mutable.
/// This usually works if the mutable isn't recursive and its var isn't used.
Expand Down Expand Up @@ -495,6 +533,9 @@ class Def : public fe::RuntimeCast<Def> {
void dot(std::ostream& os, uint32_t max = 0xFFFFFF, bool types = false) const;
/// Same as above but write to @p file or `std::cout` if @p file is `nullptr`.
void dot(const char* file = nullptr, uint32_t max = 0xFFFFFF, bool types = false) const;
void dot(const std::string& file, uint32_t max = 0xFFFFFF, bool types = false) const {
return dot(file.c_str(), max, types);
}
///@}

protected:
Expand All @@ -507,22 +548,30 @@ class Def : public fe::RuntimeCast<Def> {
///@}

private:
virtual Def* stub_(World&, Ref) { fe::unreachable(); }
virtual Ref rebuild_(World& w, Ref type, Defs ops) const = 0;

Vars free_vars(bool&, uint32_t run);
void validate();
void invalidate();
Def* unset(size_t i);
const Def** ops_ptr() const {
return reinterpret_cast<const Def**>(reinterpret_cast<char*>(const_cast<Def*>(this + 1)));
}
void finalize();
bool equal(const Def* other) const;

uint32_t mark_ = 0;
#ifndef NDEBUG
size_t curr_op_ = 0;
#endif

protected:
mutable Dbg dbg_;
union {
NormalizeFn normalizer_; ///< Axiom%s use this member to store their normalizer.
const Axiom* axiom_; /// Curried App%s of Axiom%s use this member to propagate the Axiom.
NormalizeFn normalizer_; ///< Axiom only: Axiom%s use this member to store their normalizer.
const Axiom* axiom_; ///< App only: Curried App%s of Axiom%s use this member to propagate the Axiom.
const Var* var_; ///< Mutable only: Var of a mutable.
mutable World* world_;
};
flags_t flags_;
Expand All @@ -534,11 +583,26 @@ class Def : public fe::RuntimeCast<Def> {
bool mut_ : 1;
bool external_ : 1;
unsigned dep_ : 5;
bool padding_ : 1;
bool valid_ : 1;
hash_t hash_;
u32 gid_;
u32 num_ops_;
mutable Uses uses_;

union LocalOrFreeVars {
LocalOrFreeVars() {}

Vars local; // Mutable only.
Vars free; // Immutable only.
} vars_;

union LocalOrConsumerMuts {
LocalOrConsumerMuts() {}

Muts local; // Mutable only.
Muts fv_consumers; // Immutable only.
} muts_;

const Def* type_;

friend class World;
Expand Down Expand Up @@ -741,9 +805,11 @@ class Global : public Def {
bool is_mutable() const { return flags(); }
///@}

Global* stub(World&, Ref) override;

Global* stub(Ref type) { return stub_(world(), type)->set(dbg()); }
THORIN_DEF_MIXIN(Global)

private:
Global* stub_(World&, Ref) override;
};

hash_t UseHash::operator()(Use use) const { return hash_combine(hash_begin(u16(use.index())), hash_t(use->gid())); }
Expand Down
10 changes: 8 additions & 2 deletions include/thorin/lam.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@ class Pi : public Def {
///@}

const Pi* immutabilize() override;
Pi* stub(World&, Ref) override;
Pi* stub(Ref type) { return stub_(world(), type)->set(dbg()); }

THORIN_DEF_MIXIN(Pi)

private:
Pi* stub_(World&, Ref) override;
};

/// A function.
Expand Down Expand Up @@ -167,14 +170,17 @@ class Lam : public Def {
Lam* unset() { return Def::unset()->as<Lam>(); }
///@}

Lam* stub(World&, Ref) override;
Lam* stub(Ref type) { return stub_(world(), type)->set(dbg()); }

/// @name Type Checking
///@{
void check() override;
///@}

THORIN_DEF_MIXIN(Lam)

private:
Lam* stub_(World&, Ref) override;
};

/// @name Lam
Expand Down
19 changes: 14 additions & 5 deletions include/thorin/lattice.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@ template<bool Up> class TBound : public Bound {
public:
THORIN_SETTERS(TBound)

Ref rebuild(World&, Ref, Defs) const override;
TBound* stub(World&, Ref) override;
TBound* stub(Ref type) { return stub_(world(), type)->set(dbg()); }

friend class World;
static constexpr auto Node = Up ? Node::Join : Node::Meet;

private:
Ref rebuild_(World&, Ref, Defs) const override;
TBound* stub_(World&, Ref) override;

friend class World;
};

/// Constructs a [Meet](@ref thorin::Meet) **value**.
Expand Down Expand Up @@ -128,12 +132,17 @@ template<bool Up> class TExt : public Ext {
TExt(const Def* type)
: Ext(Node, type) {}

public:
THORIN_SETTERS(TExt)

Ref rebuild(World&, Ref, Defs) const override;
TExt* stub(World&, Ref) override;
TExt* stub(Ref type) { return stub_(world(), type)->set(dbg()); }

static constexpr auto Node = Up ? Node::Top : Node::Bot;

private:
Ref rebuild_(World&, Ref, Defs) const override;
TExt* stub_(World&, Ref) override;

friend class World;
};

Expand Down
22 changes: 12 additions & 10 deletions include/thorin/pass/pass.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,35 +211,37 @@ class PassMan {
bool fixed_point_ = false;
bool proxy_ = false;

template<class P, class N> friend class FPPass;
template<class P, class M> friend class FPPass;
};

/// Inherit from this class if your Pass does **not** need state and a fixed-point iteration.
template<class P, class N> class RWPass : public Pass {
/// Inherit from this class using [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern),
/// if your Pass does **not** need state and a fixed-point iteration.
/// If you a are only interested in specific mutables, you can pass this to @p M.
template<class P, class M = Def> class RWPass : public Pass {
public:
RWPass(PassMan& man, std::string_view name)
: Pass(man, name) {}

bool inspect() const override {
if constexpr (std::is_same<N, Def>::value)
if constexpr (std::is_same<M, Def>::value)
return man().curr_mut();
else
return man().curr_mut()->template isa<N>();
return man().curr_mut()->template isa<M>();
}

N* curr_mut() const {
if constexpr (std::is_same<N, Def>::value)
M* curr_mut() const {
if constexpr (std::is_same<M, Def>::value)
return man().curr_mut();
else
return man().curr_mut()->template as<N>();
return man().curr_mut()->template as<M>();
}
};

/// Inherit from this class using [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern),
/// if you **do** need a Pass with a state and a fixed-point.
template<class P, class N> class FPPass : public RWPass<P, N> {
template<class P, class M = Def> class FPPass : public RWPass<P, M> {
public:
using Super = RWPass<P, N>;
using Super = RWPass<P, M>;
using Data = std::tuple<>; ///< Default.

FPPass(PassMan& man, std::string_view name)
Expand Down
Loading
Loading