Thorin 1.9.0
The Higher ORder INtermediate representation
Loading...
Searching...
No Matches
dump.cpp
Go to the documentation of this file.
1#include <fstream>
2
3#include <fe/assert.h>
4
5#include "thorin/driver.h"
6
8#include "thorin/fe/tok.h"
9#include "thorin/util/util.h"
10
11using namespace std::literals;
12
13// During dumping, we classify Defs according to the following logic:
14// * Inline: These Defs are *always* displayed with all of its operands "inline".
15// E.g.: (1, 2, 3).
16// * All other Defs are referenced by its name/unique_name (see id) when they appear as an operand.
17// * Mutables are either classifed as "decl" (see isa_decl).
18// In this case, recursing through the Defs' operands stops and this particular Decl is dumped as its own thing.
19// * Or - if they are not a "decl" - they are basicallally handled like immutables.
20
21namespace thorin {
22
23namespace {
24
25Def* isa_decl(const Def* def) {
26 if (auto mut = def->isa_mut()) {
27 if (mut->is_external() || mut->isa<Lam>() || (mut->sym() && mut->sym() != '_')) return mut;
28 }
29 return nullptr;
30}
31
32std::string id(const Def* def) {
33 if (def->is_external() || (!def->is_set() && def->isa<Lam>())) return def->sym().str();
34 return def->unique_name();
35}
36
37std::string_view external(const Def* def) {
38 if (def->is_external()) return ".extern "sv;
39 return ""sv;
40}
41
42/*
43 * Inline + LRPrec
44 */
45
46/// This is a wrapper to dump a Def "inline" and print it with all of its operands.
47struct Inline {
48 Inline(const Def* def, int dump_gid)
49 : def_(def)
50 , dump_gid_(dump_gid) {}
51 Inline(const Def* def)
52 : Inline(def, def->world().flags().dump_gid) {}
53
54 const Def* operator->() const { return def_; }
55 const Def* operator*() const { return def_; }
56 explicit operator bool() const {
57 if (def_->dep_const()) return true;
58
59 if (auto mut = def_->isa_mut()) {
60 if (isa_decl(mut)) return false;
61 return true;
62 }
63
64 if (auto app = def_->isa<App>()) {
65 if (app->type()->isa<Pi>()) return true; // curried apps are printed inline
66 if (app->type()->isa<Type>()) return true;
67 if (app->callee()->isa<Axiom>()) return app->callee_type()->num_doms() <= 1;
68 return false;
69 }
70
71 return true;
72 }
73
74private:
75 const Def* def_;
76 const int dump_gid_;
77
78 friend std::ostream& operator<<(std::ostream&, Inline);
79};
80
81// TODO prec is currently broken
82template<bool L> struct LRPrec {
83 LRPrec(const Def* l, const Def* r)
84 : l(l)
85 , r(r) {}
86
87private:
88 const Def* l;
89 const Def* r;
90
91 friend std::ostream& operator<<(std::ostream& os, const LRPrec& p) {
92 if constexpr (L) {
93 if (Inline(p.l) && Tok::prec(Tok::prec(p.r))[0] > Tok::prec(p.r)) return print(os, "({})", p.l);
94 return print(os, "{}", p.l);
95 } else {
96 if (Inline(p.r) && Tok::prec(p.l) > Tok::prec(Tok::prec(p.l))[1]) return print(os, "({})", p.r);
97 return print(os, "{}", p.r);
98 }
99 }
100};
101
102using LPrec = LRPrec<true>;
103using RPrec = LRPrec<false>;
104
105std::ostream& operator<<(std::ostream& os, Inline u) {
106 if (u.dump_gid_ == 2 || (u.dump_gid_ == 1 && !u->isa<Var>() && u->num_ops() != 0)) print(os, "/*{}*/", u->gid());
107 if (auto mut = u->isa_mut(); mut && !mut->is_set()) return os << "unset";
108
109 bool ascii = u->world().flags().ascii;
110 auto arw = ascii ? "->" : "→";
111 auto al = ascii ? "<<" : "«";
112 auto ar = ascii ? ">>" : "»";
113 auto pl = ascii ? "(<" : "‹";
114 auto pr = ascii ? ">)" : "›";
115
116 if (auto type = u->isa<Type>()) {
117 if (auto level = Lit::isa(type->level()); level && !ascii) {
118 if (level == 0) return print(os, "★");
119 if (level == 1) return print(os, "□");
120 }
121 return print(os, "(.Type {})", type->level());
122 } else if (u->isa<Nat>()) {
123 return print(os, ".Nat");
124 } else if (u->isa<Idx>()) {
125 return print(os, ".Idx");
126 } else if (auto ext = u->isa<Ext>()) {
127 auto x = ext->isa<Bot>() ? (ascii ? ".bot" : "⊥") : (ascii ? ".top" : "⊤");
128 if (ext->type()->isa<Nat>()) return print(os, "{}:{}", x, ext->type());
129 return print(os, "{}:({})", x, ext->type());
130 } else if (auto top = u->isa<Top>()) {
131 return print(os, "{}:({})", ascii ? ".top" : "⊤", top->type());
132 } else if (auto axiom = u->isa<Axiom>()) {
133 const auto name = axiom->sym();
134 return print(os, "{}{}", name[0] == '%' ? "" : "%", name);
135 } else if (auto lit = u->isa<Lit>()) {
136 if (lit->type()->isa<Nat>()) {
137 switch (lit->get()) {
138 // clang-format off
139 case 0x0'0000'0100_n: return os << ".i8";
140 case 0x0'0001'0000_n: return os << ".i16";
141 case 0x1'0000'0000_n: return os << ".i32";
142 // clang-format on
143 default: return print(os, "{}", lit->get());
144 }
145 } else if (auto size = Idx::size(lit->type())) {
146 if (auto s = Lit::isa(size)) {
147 switch (*s) {
148 // clang-format off
149 case 0x0'0000'0002_n: return os << (lit->get<bool>() ? ".tt" : ".ff");
150 case 0x0'0000'0100_n: return os << lit->get() << "I8";
151 case 0x0'0001'0000_n: return os << lit->get() << "I16";
152 case 0x1'0000'0000_n: return os << lit->get() << "I32";
153 case 0_n: return os << lit->get() << "I64";
154 default: {
155 os << lit->get();
156 std::vector<uint8_t> digits;
157 for (auto z = *s; z; z /= 10) digits.emplace_back(z % 10);
158
159 if (ascii) {
160 os << '_';
161 for (auto d : digits | std::ranges::views::reverse)
162 os << char('0' + d);
163 } else {
164 for (auto d : digits | std::ranges::views::reverse)
165 os << uint8_t(0xE2) << uint8_t(0x82) << (uint8_t(0x80 + d));
166 }
167 return os;
168 }
169 // clang-format on
170 }
171 }
172 }
173 if (lit->type()->isa<App>()) return print(os, "{}:({})", lit->get(), lit->type()); // HACK prec magic is broken
174 return print(os, "{}:{}", lit->get(), lit->type());
175 } else if (auto ex = u->isa<Extract>()) {
176 if (ex->tuple()->isa<Var>() && ex->index()->isa<Lit>()) return print(os, "{}", ex->unique_name());
177 return print(os, "{}#{}", ex->tuple(), ex->index());
178 } else if (auto var = u->isa<Var>()) {
179 return print(os, "{}", var->unique_name());
180 } else if (auto pi = u->isa<Pi>()) {
181 if (Pi::isa_cn(pi)) return print(os, ".Cn {}", pi->dom());
182 if (auto mut = pi->isa_mut<Pi>(); mut && mut->var())
183 return print(os, "Π {}: {} {} {}", mut->var(), pi->dom(), arw, pi->codom());
184 return print(os, "Π {} {} {}", pi->dom(), arw, pi->codom());
185 } else if (auto lam = u->isa<Lam>()) {
186 return print(os, "{}, {}", lam->filter(), lam->body());
187 } else if (auto app = u->isa<App>()) {
188 if (auto size = Idx::size(app)) {
189 if (auto l = Lit::isa(size)) {
190 switch (*l) {
191 // clang-format off
192 case 0x0'0000'0002_n: return os << ".Bool";
193 case 0x0'0000'0100_n: return os << ".I8";
194 case 0x0'0001'0000_n: return os << ".I16";
195 case 0x1'0000'0000_n: return os << ".I32";
196 case 0_n: return os << ".I64";
197 default: break;
198 // clang-format on
199 }
200 }
201 }
202 return print(os, "{} {}", LPrec(app->callee(), app), RPrec(app, app->arg()));
203 } else if (auto sigma = u->isa<Sigma>()) {
204 if (auto mut = sigma->isa_mut<Sigma>(); mut && mut->var()) {
205 size_t i = 0;
206 return print(os, "[{, }]", Elem(sigma->ops(), [&](auto op) {
207 if (auto v = mut->var(i++))
208 print(os, "{}: {}", v, op);
209 else
210 os << op;
211 }));
212 }
213 return print(os, "[{, }]", sigma->ops());
214 } else if (auto tuple = u->isa<Tuple>()) {
215 print(os, "({, })", tuple->ops());
216 return tuple->type()->isa_mut() ? print(os, ":{}", tuple->type()) : os;
217 } else if (auto arr = u->isa<Arr>()) {
218 if (auto mut = arr->isa_mut<Arr>(); mut && mut->var())
219 return print(os, "{}{}: {}; {}{}", al, mut->var(), mut->shape(), mut->body(), ar);
220 return print(os, "{}{}; {}{}", al, arr->shape(), arr->body(), ar);
221 } else if (auto pack = u->isa<Pack>()) {
222 if (auto mut = pack->isa_mut<Pack>(); mut && mut->var())
223 return print(os, "{}{}: {}; {}{}", pl, mut->var(), mut->shape(), mut->body(), pr);
224 return print(os, "{}{}; {}{}", pl, pack->shape(), pack->body(), pr);
225 } else if (auto proxy = u->isa<Proxy>()) {
226 return print(os, ".proxy#{}#{} {, }", proxy->pass(), proxy->tag(), proxy->ops());
227 } else if (auto bound = u->isa<Bound>()) {
228 auto op = bound->isa<Join>() ? "∪" : "∩"; // TODO ascii
229 if (auto mut = u->isa_mut()) print(os, "{}{}: {}", op, mut->unique_name(), mut->type());
230 return print(os, "{}({, })", op, bound->ops());
231 }
232
233 // other
234 if (u->flags() == 0) return print(os, "(.{} {, })", u->node_name(), u->ops());
235 return print(os, "(.{}#{} {, })", u->node_name(), u->flags(), u->ops());
236}
237
238/*
239 * Dumper
240 */
241
242/// This thing operates in two modes:
243/// 1. The output of decls is driven by the DepTree.
244/// 2. Alternatively, decls are output as soon as they appear somewhere during recurse%ing.
245/// Then, they are pushed to Dumper::muts.
246class Dumper {
247public:
248 Dumper(std::ostream& os, const DepTree* dep = nullptr)
249 : os(os)
250 , dep(dep) {}
251
252 void dump(Def*);
253 void dump(Lam*);
254 void dump_let(const Def*);
255 void dump_ptrn(const Def*, const Def*);
256 void recurse(const DepNode*);
257 void recurse(const Def*, bool first = false);
258
259 std::ostream& os;
260 const DepTree* dep;
261 Tab tab;
262 unique_queue<MutSet> muts;
263 DefSet defs;
264};
265
266void Dumper::dump(Def* mut) {
267 if (auto lam = mut->isa<Lam>()) {
268 dump(lam);
269 return;
270 }
271
272 auto mut_prefix = [&](const Def* def) {
273 if (def->isa<Sigma>()) return ".Sigma";
274 if (def->isa<Arr>()) return ".Arr";
275 if (def->isa<Pack>()) return ".pack";
276 if (def->isa<Pi>()) return ".Pi";
277 fe::unreachable();
278 };
279
280 auto mut_op0 = [&](const Def* def) -> std::ostream& {
281 if (auto sig = def->isa<Sigma>()) return print(os, ", {}", sig->num_ops());
282 if (auto arr = def->isa<Arr>()) return print(os, ", {}", arr->shape());
283 if (auto pack = def->isa<Pack>()) return print(os, ", {}", pack->shape());
284 if (auto pi = def->isa<Pi>()) return print(os, ", {}", pi->dom());
285 fe::unreachable();
286 };
287
288 if (!mut->is_set()) {
289 tab.print(os, "{}: {} = {{ <unset> }};", id(mut), mut->type());
290 return;
291 }
292
293 tab.print(os, "{} {}{}: {}", mut_prefix(mut), external(mut), id(mut), mut->type());
294 mut_op0(mut);
295 if (mut->var()) { // TODO rewrite with dedicated methods
296 if (auto e = mut->num_vars(); e != 1) {
297 print(os, "{, }", Elem(mut->vars(), [&](auto def) {
298 if (def)
299 os << def->unique_name();
300 else
301 os << "<TODO>";
302 }));
303 } else {
304 print(os, ", @{}", mut->var()->unique_name());
305 }
306 }
307 tab.println(os, " = {{");
308 ++tab;
309 if (dep) recurse(dep->mut2node(mut));
310 recurse(mut);
311 tab.print(os, "{, }\n", mut->ops());
312 --tab;
313 tab.print(os, "}};\n");
314}
315
316void Dumper::dump(Lam* lam) {
317 // TODO filter
318 auto ptrn = [&](auto&) { dump_ptrn(lam->var(), lam->type()->dom()); };
319
320 if (Lam::isa_cn(lam))
321 tab.println(os, ".con {}{} {}@({}) = {{", external(lam), id(lam), ptrn, lam->filter());
322 else
323 tab.println(os, ".lam {}{} {}: {} = {{", external(lam), id(lam), ptrn, lam->type()->codom());
324
325 ++tab;
326 if (lam->is_set()) {
327 if (dep) recurse(dep->mut2node(lam));
328 recurse(lam->filter());
329 recurse(lam->body(), true);
330 if (lam->body()->isa_mut())
331 tab.print(os, "{}\n", lam->body());
332 else
333 tab.print(os, "{}\n", Inline(lam->body()));
334 } else {
335 tab.print(os, " <unset>\n");
336 }
337 --tab;
338 tab.print(os, "}};\n");
339}
340
341void Dumper::dump_let(const Def* def) {
342 tab.print(os, ".let {}: {} = {};\n", def->unique_name(), def->type(), Inline(def, 0));
343}
344
345void Dumper::dump_ptrn(const Def* def, const Def* type) {
346 if (!def) {
347 os << type;
348 } else {
349 auto projs = def->tprojs();
350 if (projs.size() == 1 || std::ranges::all_of(projs, [](auto def) { return !def; })) {
351 print(os, "{}: {}", def->unique_name(), type);
352 } else {
353 size_t i = 0;
354 print(os, "{}::[{, }]", def->unique_name(),
355 Elem(projs, [&](auto proj) { dump_ptrn(proj, type->proj(i++)); }));
356 }
357 }
358}
359
360void Dumper::recurse(const DepNode* node) {
361 for (auto child : node->children())
362 if (auto mut = isa_decl(child->mut())) dump(mut);
363}
364
365void Dumper::recurse(const Def* def, bool first /*= false*/) {
366 if (auto mut = isa_decl(def)) {
367 if (!dep) muts.push(mut);
368 return;
369 }
370
371 if (!defs.emplace(def).second) return;
372
373 for (auto op : def->partial_ops()) {
374 if (!op) continue;
375 recurse(op);
376 }
377
378 if (!first && !Inline(def)) dump_let(def);
379}
380
381} // namespace
382
383/*
384 * Def
385 */
386
387std::ostream& operator<<(std::ostream& os, Ref ref) { return os << *ref; }
388
389/// This will stream @p def as an operand.
390/// This is usually `id(def)` unless it can be displayed Inline.
391std::ostream& operator<<(std::ostream& os, const Def* def) {
392 if (def == nullptr) return os << "<nullptr>";
393 if (Inline(def)) {
394 auto freezer = World::Freezer(def->world());
395 return os << Inline(def);
396 }
397 return os << id(def);
398}
399
400std::ostream& Def::stream(std::ostream& os, int max) const {
401 auto freezer = World::Freezer(world());
402 auto dumper = Dumper(os);
403
404 if (max == 0) {
405 os << this << std::endl;
406 } else if (auto mut = isa_decl(this)) {
407 dumper.muts.push(mut);
408 } else {
409 dumper.recurse(this);
410 dumper.tab.print(os, "{}\n", Inline(this));
411 --max;
412 }
413
414 for (; !dumper.muts.empty() && max > 0; --max) dumper.dump(dumper.muts.pop());
415
416 return os;
417}
418
419void Def::dump() const { std::cout << this << std::endl; }
420void Def::dump(int max) const { stream(std::cout, max) << std::endl; }
421
422void Def::write(int max, const char* file) const {
423 auto ofs = std::ofstream(file);
424 stream(ofs, max);
425}
426
427void Def::write(int max) const {
428 auto file = id(this) + ".thorin"s;
429 write(max, file.c_str());
430}
431
432/*
433 * World
434 */
435
436void World::dump(std::ostream& os) {
437 auto freezer = World::Freezer(*this);
438 auto old_gid = curr_gid();
439
440 if (flags().dump_recursive) {
441 auto dumper = Dumper(os);
442 for (const auto& [_, mut] : externals()) dumper.muts.push(mut);
443 while (!dumper.muts.empty()) dumper.dump(dumper.muts.pop());
444 } else {
445 auto dep = DepTree(*this);
446 auto dumper = Dumper(os, &dep);
447
448 for (auto [_, name] : driver().imports())
449 print(os, ".{} {};\n", driver().is_loaded(name) ? "thorin/plugin" : "import", name);
450 dumper.recurse(dep.root());
451 }
452
453 assertf(old_gid == curr_gid(), "new nodes created during dump. old_gid: {}; curr_gid: {}", old_gid, curr_gid());
454}
455
456void World::dump() { dump(std::cout); }
457
459 if (log().level() == Log::Level::Debug) dump(log().ostream());
460}
461
462void World::write(const char* file) {
463 auto ofs = std::ofstream(file);
464 dump(ofs);
465}
466
468 auto file = name().str() + ".thorin"s;
469 write(file.c_str());
470}
471
472} // namespace thorin
Base class for all Defs.
Definition def.h:222
void write(int max) const
Definition dump.cpp:427
std::ostream & stream(std::ostream &, int max) const
Definition dump.cpp:400
void dump() const
Definition dump.cpp:419
World & world() const
Definition def.cpp:421
bool is_loaded(Sym sym) const
Definition driver.h:60
const auto & imports()
Definition driver.h:47
static Ref size(Ref def)
Checks if def is a .Idx s and returns s or nullptr otherwise.
Definition def.cpp:569
static const Lam * isa_cn(Ref d)
Definition lam.h:132
static std::optional< T > isa(Ref def)
Definition def.h:726
static const Pi * isa_cn(Ref d)
Is this a continuation - i.e. is the Pi::codom thorin::Bottom?
Definition lam.h:50
Helper class to retrieve Infer::arg if present.
Definition def.h:87
static constexpr std::array< Prec, 2 > prec(Prec p)
Definition tok.h:125
void write()
Same above but file name defaults to World::name.
Definition dump.cpp:467
Log & log()
Definition world.cpp:73
Flags & flags()
Retrive compile Flags.
Definition world.cpp:74
const auto & externals() const
Definition world.h:150
const Driver & driver() const
Definition world.h:83
void dump()
Dump to std::cout.
Definition dump.cpp:456
Sym name() const
Definition world.h:86
void debug_dump()
Dump in Debug build if World::log::level is Log::Level::Debug.
Definition dump.cpp:458
u32 curr_gid() const
Manage global identifier - a unique number for each Def.
Definition world.h:91
Definition span.h:103
Ref op(trait o, Ref type)
Definition core.h:35
Definition cfg.h:11
std::ostream & print(std::ostream &os, const char *s)
Base case.
Definition print.cpp:5
GIDSet< const Def * > DefSet
Definition def.h:60
std::ostream & operator<<(std::ostream &, const CFNode *)
Definition cfg.cpp:25
TExt< false > Bot
Definition lattice.h:151
TBound< true > Join
Definition lattice.h:154
TExt< true > Top
Definition lattice.h:152
#define assertf(condition,...)
Definition print.h:161
Use to World::freeze and automatically unfreeze at the end of scope.
Definition world.h:122