MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
normalizers.cpp
Go to the documentation of this file.
1#include <algorithm>
2#include <optional>
3
4#include <fe/assert.h>
5
6#include <mim/world.h>
7
9
10#include "mim/plug/ord/ord.h"
11
12namespace mim::plug::ord {
13
14namespace {
15
16/// Decides whether the keys @p k1 and @p k2 are equal; `std::nullopt` means "don't know".
17/// Distinct literals are distinct keys, as the key's `lt` is assumed to be a strict total order.
18std::optional<bool> key_eq(const Def* k1, const Def* k2) {
19 if (k1 == k2) return true;
20 if (k1->isa<Lit>() && k2->isa<Lit>()) return false;
21 return {};
22}
23
24} // namespace
25
26template<init id>
27const Def* normalize_init(const Def* type, const Def* callee, const Def* arg) {
28 auto& w = type->world();
29 if (auto n = Lit::isa(callee->as<App>()->arg())) {
30 auto key = [](const Def* kv) { return id == init::map ? kv->proj(2, 0) : kv; };
31 auto kvs = DefVec();
32 for (auto kv : arg->projs(*n))
33 if (auto i = std::ranges::find_if(kvs, [&](const Def* cur) { return key(cur) == key(kv); }); i != kvs.end())
34 *i = kv;
35 else
36 kvs.emplace_back(kv);
37 if (kvs.size() != *n) return w.app(w.app(callee->as<App>()->callee(), w.lit_nat(kvs.size())), kvs);
38 }
39 return w.raw_app(type, callee, arg);
40}
41
42template<size>
43const Def* normalize_size(const Def*, const Def*, const Def* arg) {
44 if (auto init = Axm::isa<ord::init>(arg)) return init->decurry()->arg();
45 return nullptr;
46}
47
48const Def* normalize_get(const Def* type, const Def* callee, const Def* arg) {
49 auto& w = arg->world();
50 auto [map, k] = arg->projs<2>();
51
52 if (auto init = Axm::isa<ord::init>(map)) {
53 if (auto n = Lit::isa(init->decurry()->arg())) {
54 auto kvs = init->arg();
55 for (auto kv : kvs->projs(*n))
56 if (auto [key, val] = kv->projs<2>(); key == k) return w.inj(type, option::Opt_ctor::Some, val);
57 // FIXME a closed kvs doesn't rule out an open k matching at runtime.
58 if (kvs->is_closed()) return w.inj(type, option::Opt_ctor::None, w.tuple());
59 }
60 }
61
62 // get(insert(m, (k', v)), k) = Some v if k' = k
63 // = get(m, k) if k' ≠ k
64 if (auto ins = Axm::isa(insert::map, map)) {
65 auto [m, kv] = ins->args<2>();
66 auto [k_, v] = kv->projs<2>();
67 if (auto eq = key_eq(k_, k))
68 return *eq ? w.inj(type, option::Opt_ctor::Some, v) : w.app(callee, w.tuple({m, k}));
69 }
70
71 return nullptr;
72}
73
74template<contains id>
75const Def* normalize_contains(const Def*, const Def* callee, const Def* arg) {
76 auto& w = arg->world();
77 auto [c, k] = arg->projs<2>();
78
79 if (auto init = Axm::isa<ord::init>(c)) {
80 if (auto n = Lit::isa(init->decurry()->arg())) {
81 auto kvs = init->arg();
82 for (auto kv : kvs->projs(*n)) {
83 auto key = id == contains::map ? kv->proj(2, 0) : kv;
84 if (key == k) return w.lit_tt();
85 }
86 // FIXME a closed kvs doesn't rule out an open k matching at runtime.
87 return kvs->is_closed() ? w.lit_ff() : nullptr;
88 }
89
90 if (auto pack = init->arg()->isa_imm<Pack>()) w.log().w("Pack not yet implemented: {}", pack);
91 }
92
93 // contains(insert(m, k'), k) = tt if k' = k
94 // = contains(m, k) if k' ≠ k
95 if (auto ins = Axm::isa(id == contains::map ? insert::map : insert::set, c)) {
96 auto [m, kv] = ins->args<2>();
97 auto key = id == contains::map ? kv->proj(2, 0) : kv;
98 if (auto eq = key_eq(key, k)) return *eq ? w.lit_tt() : w.app(callee, w.tuple({m, k}));
99 }
100
101 return nullptr;
102}
103
104template<insert id>
105const Def* normalize_insert(const Def* type, const Def* callee, const Def* arg) {
106 auto& w = type->world();
107 auto [ms, kv] = arg->projs<2>();
108
109 if (auto init = Axm::isa<ord::init>(ms)) {
110 auto [n, kvs] = init->uncurry_args<2>();
111 if (auto l = Lit::isa(n)) {
112 auto key = id == ord::insert::map ? kv->proj(2, 0) : kv;
113 auto new_ops = DefVec();
114 bool updated = false;
115 for (auto cur : kvs->projs(*l)) {
116 auto cur_key = id == ord::insert::map ? cur->proj(2, 0) : cur;
117 if (key == cur_key) updated = true;
118 new_ops.emplace_back(key == cur_key ? kv : cur);
119 }
120 if (!updated) new_ops.emplace_back(kv);
121 return w.app(w.app(init->decurry()->callee(), w.lit_nat(new_ops.size())), new_ops);
122 }
123
124 if (auto pack = init->arg()->isa_imm<Pack>()) w.log().w("Pack not yet implemented: {}", pack);
125 }
126
127 // insert(insert(m, k'), k) = insert(m, k) if k' = k
128 // = insert(insert(m, k), k') if k' ≠ k and k < k' (canonical order of literal keys)
129 if (auto ins = Axm::isa(id, ms)) {
130 auto [m, kv_] = ins->args<2>();
131 auto key = [](const Def* kv) { return id == ord::insert::map ? kv->proj(2, 0) : kv; };
132 if (auto eq = key_eq(key(kv_), key(kv))) {
133 if (*eq) return w.app(callee, w.tuple({m, kv}));
134 if (*Lit::isa(key(kv)) < *Lit::isa(key(kv_)))
135 return w.app(callee, w.tuple({w.app(callee, w.tuple({m, kv})), kv_}));
136 }
137 }
138
139 return {};
140}
141
143
144} // namespace mim::plug::ord
const Def * callee() const
Definition lam.h:237
const Def * arg() const
Definition lam.h:251
static auto isa(const Def *def)
Definition axm.h:112
Base class for all Defs.
Definition def.h:289
World & world() const noexcept
Definition def.h:1105
auto projs(Projector auto f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
Definition def.h:457
static std::optional< T > isa(const Def *def)
Definition def.h:976
A (possibly paramterized) Tuple.
Definition tuple.h:332
The ord Plugin
const Def * normalize_contains(const Def *, const Def *callee, const Def *arg)
const Def * normalize_size(const Def *, const Def *, const Def *arg)
const Def * normalize_insert(const Def *type, const Def *callee, const Def *arg)
const Def * normalize_init(const Def *type, const Def *callee, const Def *arg)
const Def * normalize_get(const Def *type, const Def *callee, const Def *arg)
fe::Vector< const Def * > DefVec
Definition def.h:98
#define MIM_ord_NORMALIZER_IMPL
Definition autogen.h:122