MimIR 0.1
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
ll.cpp
Go to the documentation of this file.
2
3#include <deque>
4#include <fstream>
5#include <iomanip>
6#include <ranges>
7
8#include <absl/container/btree_set.h>
9
10#include <mim/plug/clos/clos.h>
11#include <mim/plug/math/math.h>
12#include <mim/plug/mem/mem.h>
13
14#include "mim/be/emitter.h"
15#include "mim/util/print.h"
16#include "mim/util/sys.h"
17
18#include "mim/plug/core/core.h"
19
20// Lessons learned:
21// * **Always** follow all ops - even if you actually want to ignore one.
22// Otherwise, you might end up with an incorrect schedule.
23// This was the case for an Extract of type Mem.
24// While we want to ignore the value obtained from that, since there is no Mem value in LLVM,
25// we still want to **first** recursively emit code for its operands and **then** ignore the Extract itself.
26// * i1 has a different meaning in LLVM then in Mim:
27// * Mim: {0, 1} = i1
28// * LLVM: {0, -1} = i1
29// This is a problem when, e.g., using an index of type i1 as LLVM thinks like this:
30// getelementptr ..., i1 1 == getelementptr .., i1 -1
31using namespace std::string_literals;
32
33namespace mim::ll {
34
35namespace clos = mim::plug::clos;
36namespace core = mim::plug::core;
37namespace math = mim::plug::math;
38namespace mem = mim::plug::mem;
39
40namespace {
41const char* math_suffix(const Def* type) {
42 if (auto w = math::isa_f(type)) {
43 switch (*w) {
44 case 32: return "f";
45 case 64: return "";
46 }
47 }
48 error("unsupported foating point type '{}'", type);
49}
50
51const char* llvm_suffix(const Def* type) {
52 if (auto w = math::isa_f(type)) {
53 switch (*w) {
54 case 16: return ".f16";
55 case 32: return ".f32";
56 case 64: return ".f64";
57 }
58 }
59 error("unsupported foating point type '{}'", type);
60}
61
62// [%mem.M 0, T] => T
63// TODO there may be more instances where we have to deal with this trickery
64const Def* isa_mem_sigma_2(const Def* type) {
65 if (auto sigma = type->isa<Sigma>())
66 if (sigma->num_ops() == 2 && Axm::isa<mem::M>(sigma->op(0))) return sigma->op(1);
67 return {};
68}
69} // namespace
70
71struct BB {
72 BB() = default;
73 BB(const BB&) = delete;
74 BB(BB&& other) noexcept = default;
75 BB& operator=(BB other) noexcept { return swap(*this, other), *this; }
76
77 std::deque<std::ostringstream>& head() { return parts[0]; }
78 std::deque<std::ostringstream>& body() { return parts[1]; }
79 std::deque<std::ostringstream>& tail() { return parts[2]; }
80
81 template<class... Args>
82 std::string assign(std::string_view name, const char* s, Args&&... args) {
83 print(print(body().emplace_back(), "{} = ", name), s, std::forward<Args>(args)...);
84 return std::string(name);
85 }
86
87 template<class... Args>
88 void tail(const char* s, Args&&... args) {
89 print(tail().emplace_back(), s, std::forward<Args>(args)...);
90 }
91
92 friend void swap(BB& a, BB& b) noexcept {
93 using std::swap;
94 swap(a.phis, b.phis);
95 swap(a.parts, b.parts);
96 }
97
99 std::array<std::deque<std::ostringstream>, 3> parts;
100};
101
102class Emitter : public mim::Emitter<std::string, std::string, BB, Emitter> {
103public:
105
106 Emitter(World& world, std::ostream& ostream)
107 : Super(world, "llvm_emitter", ostream) {}
108
109 bool is_valid(std::string_view s) { return !s.empty(); }
110 void start() override;
111 void emit_imported(Lam*);
112 void emit_epilogue(Lam*);
113 std::string emit_bb(BB&, const Def*);
114 std::string prepare();
115 void finalize();
116
117 template<class... Args>
118 void declare(const char* s, Args&&... args) {
119 std::ostringstream decl;
120 print(decl << "declare ", s, std::forward<Args>(args)...);
121 decls_.emplace(decl.str());
122 }
123
124private:
125 std::string id(const Def*, bool force_bb = false) const;
126 std::string convert(const Def*);
127 std::string convert_ret_pi(const Pi*);
128
129 absl::btree_set<std::string> decls_;
130 std::ostringstream type_decls_;
131 std::ostringstream vars_decls_;
132 std::ostringstream func_decls_;
133 std::ostringstream func_impls_;
134};
135
136/*
137 * convert
138 */
139
140std::string Emitter::id(const Def* def, bool force_bb /*= false*/) const {
141 if (auto global = def->isa<Global>()) return "@" + global->unique_name();
142
143 if (auto lam = def->isa_mut<Lam>(); lam && !force_bb) {
144 if (lam->type()->ret_pi()) {
145 if (lam->is_external() || !lam->is_set())
146 return "@"s + lam->sym().str(); // TODO or use is_internal or sth like that?
147 return "@"s + lam->unique_name();
148 }
149 }
150
151 return "%"s + def->unique_name();
152}
153
154std::string Emitter::convert(const Def* type) {
155 if (auto i = types_.find(type); i != types_.end()) return i->second;
156
157 assert(!Axm::isa<mem::M>(type));
158 std::ostringstream s;
159 std::string name;
160
161 if (type->isa<Nat>()) {
162 return types_[type] = "i64";
163 } else if (auto size = Idx::isa(type)) {
164 return types_[type] = "i" + std::to_string(*Idx::size2bitwidth(size));
165 } else if (auto w = math::isa_f(type)) {
166 switch (*w) {
167 case 16: return types_[type] = "half";
168 case 32: return types_[type] = "float";
169 case 64: return types_[type] = "double";
170 default: fe::unreachable();
171 }
172 } else if (auto ptr = Axm::isa<mem::Ptr>(type)) {
173 auto [pointee, addr_space] = ptr->args<2>();
174 // TODO addr_space
175 print(s, "{}*", convert(pointee));
176 } else if (auto arr = type->isa<Arr>()) {
177 auto t_elem = convert(arr->body());
178 u64 size = 0;
179 if (auto arity = Lit::isa(arr->arity())) size = *arity;
180 print(s, "[{} x {}]", size, t_elem);
181 } else if (auto pi = type->isa<Pi>()) {
182 assert(Pi::isa_returning(pi) && "should never have to convert type of BB");
183 print(s, "{} (", convert_ret_pi(pi->ret_pi()));
184
185 if (auto t = isa_mem_sigma_2(pi->dom()))
186 s << convert(t);
187 else {
188 auto doms = pi->doms();
189 for (auto sep = ""; auto dom : doms.view().rsubspan(1)) {
190 if (Axm::isa<mem::M>(dom)) continue;
191 s << sep << convert(dom);
192 sep = ", ";
193 }
194 }
195 s << ")*";
196 } else if (auto t = isa_mem_sigma_2(type)) {
197 return convert(t);
198 } else if (auto sigma = type->isa<Sigma>()) {
199 if (sigma->isa_mut()) {
200 name = id(sigma);
201 types_[sigma] = name;
202 print(s, "{} = type", name);
203 }
204
205 print(s, "{{");
206 for (auto sep = ""; auto t : sigma->ops()) {
207 if (Axm::isa<mem::M>(t)) continue;
208 s << sep << convert(t);
209 sep = ", ";
210 }
211 print(s, "}}");
212 } else {
213 fe::unreachable();
214 }
215
216 if (name.empty()) return types_[type] = s.str();
217
218 assert(!s.str().empty());
219 type_decls_ << s.str() << '\n';
220 return types_[type] = name;
221}
222
223std::string Emitter::convert_ret_pi(const Pi* pi) {
224 auto dom = mem::strip_mem_ty(pi->dom());
225 if (dom == world().sigma()) return "void";
226 return convert(dom);
227}
228
229/*
230 * emit
231 */
232
234 Super::start();
235
236 ostream() << type_decls_.str() << '\n';
237 for (auto&& decl : decls_)
238 ostream() << decl << '\n';
239 ostream() << func_decls_.str() << '\n';
240 ostream() << vars_decls_.str() << '\n';
241 ostream() << func_impls_.str() << '\n';
242}
243
245 // TODO merge with declare method
246 print(func_decls_, "declare {} {}(", convert_ret_pi(lam->type()->ret_pi()), id(lam));
247
248 auto doms = lam->doms();
249 for (auto sep = ""; auto dom : doms.view().rsubspan(1)) {
250 if (Axm::isa<mem::M>(dom)) continue;
251 print(func_decls_, "{}{}", sep, convert(dom));
252 sep = ", ";
253 }
254
255 print(func_decls_, ")\n");
256}
257
258std::string Emitter::prepare() {
259 print(func_impls_, "define {} {}(", convert_ret_pi(root()->type()->ret_pi()), id(root()));
260
261 auto vars = root()->vars();
262 for (auto sep = ""; auto var : vars.view().rsubspan(1)) {
263 if (Axm::isa<mem::M>(var->type())) continue;
264 auto name = id(var);
265 locals_[var] = name;
266 print(func_impls_, "{}{} {}", sep, convert(var->type()), name);
267 sep = ", ";
268 }
269
270 print(func_impls_, ") {{\n");
271 return root()->unique_name();
272}
273
275 for (auto& [lam, bb] : lam2bb_) {
276 for (const auto& [phi, args] : bb.phis) {
277 print(bb.head().emplace_back(), "{} = phi {} ", id(phi), convert(phi->type()));
278 for (auto sep = ""; const auto& [arg, pred] : args) {
279 print(bb.head().back(), "{}[ {}, {} ]", sep, arg, pred);
280 sep = ", ";
281 }
282 }
283 }
284
285 for (auto mut : Scheduler::schedule(nest())) {
286 if (auto lam = mut->isa_mut<Lam>()) {
287 assert(lam2bb_.contains(lam));
288 auto& bb = lam2bb_[lam];
289 print(func_impls_, "{}:\n", lam->unique_name());
290
291 ++tab;
292 for (const auto& part : bb.parts)
293 for (const auto& line : part)
294 tab.print(func_impls_, "{}\n", line.str());
295 --tab;
296 func_impls_ << std::endl;
297 }
298 }
299
300 print(func_impls_, "}}\n\n");
301}
302
304 auto app = lam->body()->as<App>();
305 auto& bb = lam2bb_[lam];
306
307 if (app->callee() == root()->ret_var()) { // return
308 std::vector<std::string> values;
309 std::vector<const Def*> types;
310
311 for (auto arg : app->args()) {
312 if (auto val = emit_unsafe(arg); !val.empty()) {
313 values.emplace_back(val);
314 types.emplace_back(arg->type());
315 }
316 }
317
318 switch (values.size()) {
319 case 0: return bb.tail("ret void");
320 case 1: return bb.tail("ret {} {}", convert(types[0]), values[0]);
321 default: {
322 std::string prev = "undef";
323 auto type = convert(world().sigma(types));
324 for (size_t i = 0, n = values.size(); i != n; ++i) {
325 auto v_elem = values[i];
326 auto t_elem = convert(types[i]);
327 auto namei = "%ret_val." + std::to_string(i);
328 bb.tail("{} = insertvalue {} {}, {} {}, {}", namei, type, prev, t_elem, v_elem, i);
329 prev = namei;
330 }
331
332 bb.tail("ret {} {}", type, prev);
333 }
334 }
335 } else if (auto dispatch = Dispatch(app)) {
336 for (auto callee : dispatch.tuple()->projs([](const Def* def) { return def->isa_mut<Lam>(); })) {
337 size_t n = callee->num_tvars();
338 for (size_t i = 0; i != n; ++i) {
339 if (auto arg = emit_unsafe(app->arg(n, i)); !arg.empty()) {
340 auto phi = callee->var(n, i);
341 assert(!Axm::isa<mem::M>(phi->type()));
342 lam2bb_[callee].phis[phi].emplace_back(arg, id(lam, true));
343 locals_[phi] = id(phi);
344 }
345 }
346 }
347
348 auto v_index = emit(dispatch.index());
349 size_t n = dispatch.num_targets();
350 auto bbs = absl::FixedArray<std::string>(n);
351 for (size_t i = 0; i != n; ++i)
352 bbs[i] = emit(dispatch.target(i));
353
354 if (auto branch = Branch(app)) return bb.tail("br i1 {}, label {}, label {}", v_index, bbs[1], bbs[0]);
355
356 auto t_index = convert(dispatch.index()->type());
357 bb.tail("switch {} {}, label {} [ ", t_index, v_index, bbs[0]);
358 for (size_t i = 1; i != n; ++i)
359 print(bb.tail().back(), "{} {}, label {} ", t_index, std::to_string(i), bbs[i]);
360 print(bb.tail().back(), "]");
361 } else if (app->callee()->isa<Bot>()) {
362 return bb.tail("ret ; bottom: unreachable");
363 } else if (auto callee = Lam::isa_mut_basicblock(app->callee())) { // ordinary jump
364 size_t n = callee->num_tvars();
365 for (size_t i = 0; i != n; ++i) {
366 if (auto arg = emit_unsafe(app->arg(n, i)); !arg.empty()) {
367 auto phi = callee->var(n, i);
368 assert(!Axm::isa<mem::M>(phi->type()));
369 lam2bb_[callee].phis[phi].emplace_back(arg, id(lam, true));
370 locals_[phi] = id(phi);
371 }
372 }
373 return bb.tail("br label {}", id(callee));
374 } else if (auto longjmp = Axm::isa<clos::longjmp>(app)) {
375 declare("void @longjmp(i8*, i32) noreturn");
376
377 auto [mem, jbuf, tag] = app->args<3>();
378 emit_unsafe(mem);
379 auto v_jb = emit(jbuf);
380 auto v_tag = emit(tag);
381 bb.tail("call void @longjmp(i8* {}, i32 {})", v_jb, v_tag);
382 return bb.tail("unreachable");
383 } else if (Pi::isa_returning(app->callee_type())) { // function call
384 auto v_callee = emit(app->callee());
385
386 std::vector<std::string> args;
387 auto app_args = app->args();
388 for (auto arg : app_args.view().rsubspan(1))
389 if (auto v_arg = emit_unsafe(arg); !v_arg.empty()) args.emplace_back(convert(arg->type()) + " " + v_arg);
390
391 if (app->args().back()->isa<Bot>()) {
392 // TODO: Perhaps it'd be better to simply η-wrap this prior to the BE...
393 assert(convert_ret_pi(app->callee_type()->ret_pi()) == "void");
394 bb.tail("call void {}({, })", v_callee, args);
395 return bb.tail("unreachable");
396 }
397
398 auto ret_lam = app->args().back()->as_mut<Lam>();
399 size_t num_vars = ret_lam->num_vars();
400 size_t n = 0;
401 DefVec values(num_vars);
402 DefVec types(num_vars);
403 for (auto var : ret_lam->vars()) {
404 if (Axm::isa<mem::M>(var->type())) continue;
405 values[n] = var;
406 types[n] = var->type();
407 ++n;
408 }
409
410 if (n == 0) {
411 bb.tail("call void {}({, })", v_callee, args);
412 } else {
413 auto name = "%" + app->unique_name() + "ret";
414 auto t_ret = convert_ret_pi(ret_lam->type());
415 bb.tail("{} = call {} {}({, })", name, t_ret, v_callee, args);
416
417 for (size_t i = 0, j = 0, e = ret_lam->num_vars(); i != e; ++i) {
418 auto phi = ret_lam->var(i);
419 if (Axm::isa<mem::M>(phi->type())) continue;
420
421 auto namej = name;
422 if (e > 2) {
423 namej += '.' + std::to_string(j);
424 bb.tail("{} = extractvalue {} {}, {}", namej, t_ret, name, j);
425 }
426 assert(!Axm::isa<mem::M>(phi->type()));
427 lam2bb_[ret_lam].phis[phi].emplace_back(namej, id(lam, true));
428 locals_[phi] = id(phi);
429 ++j;
430 }
431 }
432
433 return bb.tail("br label {}", id(ret_lam));
434 }
435}
436
437std::string Emitter::emit_bb(BB& bb, const Def* def) {
438 if (auto lam = def->isa<Lam>()) return id(lam);
439
440 auto name = id(def);
441 std::string op;
442
443 auto emit_tuple = [&](const Def* tuple) {
444 if (isa_mem_sigma_2(tuple->type())) {
445 emit_unsafe(tuple->proj(2, 0));
446 return emit(tuple->proj(2, 1));
447 }
448
449 if (tuple->is_closed()) {
450 bool is_array = tuple->type()->isa<Arr>();
451
452 std::string s;
453 s += is_array ? "[" : "{";
454 auto sep = "";
455 for (size_t i = 0, n = tuple->num_projs(); i != n; ++i) {
456 auto e = tuple->proj(n, i);
457 if (auto v_elem = emit_unsafe(e); !v_elem.empty()) {
458 auto t_elem = convert(e->type());
459 s += sep + t_elem + " " + v_elem;
460 sep = ", ";
461 }
462 }
463
464 return s += is_array ? "]" : "}";
465 }
466
467 std::string prev = "undef";
468 auto t = convert(tuple->type());
469 for (size_t src = 0, dst = 0, n = tuple->num_projs(); src != n; ++src) {
470 auto e = tuple->proj(n, src);
471 if (auto elem = emit_unsafe(e); !elem.empty()) {
472 auto elem_t = convert(e->type());
473 // TODO: check dst vs src
474 auto namei = name + "." + std::to_string(dst);
475 prev = bb.assign(namei, "insertvalue {} {}, {} {}, {}", t, prev, elem_t, elem, dst);
476 dst++;
477 }
478 }
479 return prev;
480 };
481
482 if (def->isa<Var>()) {
483 auto ts = def->type()->projs();
484 if (std::ranges::any_of(ts, [](auto t) { return Axm::isa<mem::M>(t); })) return {};
485 return emit_tuple(def);
486 }
487
488 auto emit_gep_index = [&](const Def* index) {
489 auto v_i = emit(index);
490 auto t_i = convert(index->type());
491
492 if (auto size = Idx::isa(index->type())) {
493 if (auto w = Idx::size2bitwidth(size); w && *w < 64) {
494 v_i = bb.assign(name + ".zext",
495 "zext {} {} to i{} ; add one more bit for gep index as it is treated as signed value",
496 t_i, v_i, *w + 1);
497 t_i = "i" + std::to_string(*w + 1);
498 }
499 }
500
501 return std::pair(v_i, t_i);
502 };
503
504 if (auto lit = def->isa<Lit>()) {
505 if (lit->type()->isa<Nat>() || Idx::isa(lit->type())) {
506 return std::to_string(lit->get());
507 } else if (auto w = math::isa_f(lit->type())) {
508 std::stringstream s;
509 u64 hex;
510
511 switch (*w) {
512 case 16:
513 s << "0xH" << std::setfill('0') << std::setw(4) << std::right << std::hex << lit->get<u16>();
514 return s.str();
515 case 32: {
516 hex = std::bit_cast<u64>(f64(lit->get<f32>()));
517 break;
518 }
519 case 64: hex = lit->get<u64>(); break;
520 default: fe::unreachable();
521 }
522
523 s << "0x" << std::setfill('0') << std::setw(16) << std::right << std::hex << hex;
524 return s.str();
525 }
526 fe::unreachable();
527 } else if (def->isa<Bot>()) {
528 return "undef";
529 } else if (auto top = def->isa<Top>()) {
530 if (Axm::isa<mem::M>(top->type())) return {};
531 // bail out to error below
532 } else if (auto tuple = def->isa<Tuple>()) {
533 return emit_tuple(tuple);
534 } else if (auto pack = def->isa<Pack>()) {
535 if (auto lit = Lit::isa(pack->body()); lit && *lit == 0) return "zeroinitializer";
536 return emit_tuple(pack);
537 } else if (auto sel = Select(def)) {
538 auto t = convert(sel.extract()->type());
539 auto [elem_a, elem_b] = sel.pair()->projs<2>([&](auto e) { return emit_unsafe(e); });
540 auto cond_t = convert(sel.cond()->type());
541 auto cond = emit(sel.cond());
542 return bb.assign(name, "select {} {}, {} {}, {} {}", cond_t, cond, t, elem_b, t, elem_a);
543 } else if (auto extract = def->isa<Extract>()) {
544 auto tuple = extract->tuple();
545 auto index = extract->index();
546 auto v_tup = emit_unsafe(tuple);
547
548 // this exact location is important: after emitting the tuple -> ordering of mem ops
549 // before emitting the index, as it might be a weird value for mem vars.
550 if (Axm::isa<mem::M>(extract->type())) return {};
551
552 auto t_tup = convert(tuple->type());
553 if (auto li = Lit::isa(index)) {
554 if (isa_mem_sigma_2(tuple->type())) return v_tup;
555 // Adjust index, if mem is present.
556 auto v_i = Axm::isa<mem::M>(tuple->proj(0)->type()) ? std::to_string(*li - 1) : std::to_string(*li);
557 return bb.assign(name, "extractvalue {} {}, {}", t_tup, v_tup, v_i);
558 }
559
560 auto t_elem = convert(extract->type());
561 auto [v_i, t_i] = emit_gep_index(index);
562
563 print(lam2bb_[root()].body().emplace_front(),
564 "{}.alloca = alloca {} ; copy to alloca to emulate extract with store + gep + load", name, t_tup);
565 print(bb.body().emplace_back(), "store {} {}, {}* {}.alloca", t_tup, v_tup, t_tup, name);
566 print(bb.body().emplace_back(), "{}.gep = getelementptr inbounds {}, {}* {}.alloca, i64 0, {} {}", name, t_tup,
567 t_tup, name, t_i, v_i);
568 return bb.assign(name, "load {}, {}* {}.gep", t_elem, t_elem, name);
569 } else if (auto insert = def->isa<Insert>()) {
570 assert(!Axm::isa<mem::M>(insert->tuple()->proj(0)->type()));
571 auto t_tup = convert(insert->tuple()->type());
572 auto t_val = convert(insert->value()->type());
573 auto v_tup = emit(insert->tuple());
574 auto v_val = emit(insert->value());
575 if (auto idx = Lit::isa(insert->index())) {
576 auto v_idx = emit(insert->index());
577 return bb.assign(name, "insertvalue {} {}, {} {}, {}", t_tup, v_tup, t_val, v_val, v_idx);
578 } else {
579 auto t_elem = convert(insert->value()->type());
580 auto [v_i, t_i] = emit_gep_index(insert->index());
581 print(lam2bb_[root()].body().emplace_front(),
582 "{}.alloca = alloca {} ; copy to alloca to emulate insert with store + gep + load", name, t_tup);
583 print(bb.body().emplace_back(), "store {} {}, {}* {}.alloca", t_tup, v_tup, t_tup, name);
584 print(bb.body().emplace_back(), "{}.gep = getelementptr inbounds {}, {}* {}.alloca, i64 0, {} {}", name,
585 t_tup, t_tup, name, t_i, v_i);
586 print(bb.body().emplace_back(), "store {} {}, {}* {}.gep", t_val, v_val, t_val, name);
587 return bb.assign(name, "load {}, {}* {}.alloca", t_tup, t_tup, name);
588 }
589 } else if (auto global = def->isa<Global>()) {
590 auto v_init = emit(global->init());
591 auto [pointee, addr_space] = Axm::as<mem::Ptr>(global->type())->args<2>();
592 print(vars_decls_, "{} = global {} {}\n", name, convert(pointee), v_init);
593 return globals_[global] = name;
594 } else if (auto nat = Axm::isa<core::nat>(def)) {
595 auto [a, b] = nat->args<2>([this](auto def) { return emit(def); });
596
597 switch (nat.id()) {
598 case core::nat::add: op = "add"; break;
599 case core::nat::sub: op = "sub"; break;
600 case core::nat::mul: op = "mul"; break;
601 }
602
603 return bb.assign(name, "{} nsw nuw i64 {}, {}", op, a, b);
604 } else if (auto ncmp = Axm::isa<core::ncmp>(def)) {
605 auto [a, b] = ncmp->args<2>([this](auto def) { return emit(def); });
606 op = "icmp ";
607
608 switch (ncmp.id()) {
609 // clang-format off
610 case core::ncmp::e: op += "eq" ; break;
611 case core::ncmp::ne: op += "ne" ; break;
612 case core::ncmp::g: op += "ugt"; break;
613 case core::ncmp::ge: op += "uge"; break;
614 case core::ncmp::l: op += "ult"; break;
615 case core::ncmp::le: op += "ule"; break;
616 // clang-format on
617 default: fe::unreachable();
618 }
619
620 return bb.assign(name, "{} i64 {}, {}", op, a, b);
621 } else if (auto idx = Axm::isa<core::idx>(def)) {
622 auto x = emit(idx->arg());
623 auto s = *Idx::size2bitwidth(Idx::isa(idx->type()));
624 auto t = convert(idx->type());
625 if (s < 64) return bb.assign(name, "trunc i64 {} to {}", x, t);
626 return x;
627 } else if (auto bit1 = Axm::isa<core::bit1>(def)) {
628 assert(bit1.id() == core::bit1::neg);
629 auto x = emit(bit1->arg());
630 auto t = convert(bit1->type());
631 return bb.assign(name, "xor {} -1, {}", t, x);
632 } else if (auto bit2 = Axm::isa<core::bit2>(def)) {
633 auto [a, b] = bit2->args<2>([this](auto def) { return emit(def); });
634 auto t = convert(bit2->type());
635
636 auto neg = [&](std::string_view x) { return bb.assign(name + ".neg", "xor {} -1, {}", t, x); };
637
638 switch (bit2.id()) {
639 // clang-format off
640 case core::bit2::and_: return bb.assign(name, "and {} {}, {}", t, a, b);
641 case core::bit2:: or_: return bb.assign(name, "or {} {}, {}", t, a, b);
642 case core::bit2::xor_: return bb.assign(name, "xor {} {}, {}", t, a, b);
643 case core::bit2::nand: return neg(bb.assign(name, "and {} {}, {}", t, a, b));
644 case core::bit2:: nor: return neg(bb.assign(name, "or {} {}, {}", t, a, b));
645 case core::bit2::nxor: return neg(bb.assign(name, "xor {} {}, {}", t, a, b));
646 case core::bit2:: iff: return bb.assign(name, "and {} {}, {}", neg(a), b);
647 case core::bit2::niff: return bb.assign(name, "or {} {}, {}", neg(a), b);
648 // clang-format on
649 default: fe::unreachable();
650 }
651 } else if (auto shr = Axm::isa<core::shr>(def)) {
652 auto [a, b] = shr->args<2>([this](auto def) { return emit(def); });
653 auto t = convert(shr->type());
654
655 switch (shr.id()) {
656 case core::shr::a: op = "ashr"; break;
657 case core::shr::l: op = "lshr"; break;
658 }
659
660 return bb.assign(name, "{} {} {}, {}", op, t, a, b);
661 } else if (auto wrap = Axm::isa<core::wrap>(def)) {
662 auto [mode, ab] = wrap->uncurry_args<2>();
663 auto [a, b] = ab->projs<2>([this](auto def) { return emit(def); });
664 auto t = convert(wrap->type());
665 auto lmode = Lit::as(mode);
666
667 switch (wrap.id()) {
668 case core::wrap::add: op = "add"; break;
669 case core::wrap::sub: op = "sub"; break;
670 case core::wrap::mul: op = "mul"; break;
671 case core::wrap::shl: op = "shl"; break;
672 }
673
674 if (lmode & core::Mode::nuw) op += " nuw";
675 if (lmode & core::Mode::nsw) op += " nsw";
676
677 return bb.assign(name, "{} {} {}, {}", op, t, a, b);
678 } else if (auto div = Axm::isa<core::div>(def)) {
679 auto [m, xy] = div->args<2>();
680 auto [x, y] = xy->projs<2>();
681 auto t = convert(x->type());
682 emit_unsafe(m);
683 auto a = emit(x);
684 auto b = emit(y);
685
686 switch (div.id()) {
687 case core::div::sdiv: op = "sdiv"; break;
688 case core::div::udiv: op = "udiv"; break;
689 case core::div::srem: op = "srem"; break;
690 case core::div::urem: op = "urem"; break;
691 }
692
693 return bb.assign(name, "{} {} {}, {}", op, t, a, b);
694 } else if (auto icmp = Axm::isa<core::icmp>(def)) {
695 auto [a, b] = icmp->args<2>([this](auto def) { return emit(def); });
696 auto t = convert(icmp->arg(0)->type());
697 op = "icmp ";
698
699 switch (icmp.id()) {
700 // clang-format off
701 case core::icmp::e: op += "eq" ; break;
702 case core::icmp::ne: op += "ne" ; break;
703 case core::icmp::sg: op += "sgt"; break;
704 case core::icmp::sge: op += "sge"; break;
705 case core::icmp::sl: op += "slt"; break;
706 case core::icmp::sle: op += "sle"; break;
707 case core::icmp::ug: op += "ugt"; break;
708 case core::icmp::uge: op += "uge"; break;
709 case core::icmp::ul: op += "ult"; break;
710 case core::icmp::ule: op += "ule"; break;
711 // clang-format on
712 default: fe::unreachable();
713 }
714
715 return bb.assign(name, "{} {} {}, {}", op, t, a, b);
716 } else if (auto extr = Axm::isa<core::extrema>(def)) {
717 auto [x, y] = extr->args<2>();
718 auto t = convert(x->type());
719 auto a = emit(x);
720 auto b = emit(y);
721 std::string f = "llvm.";
722 switch (extr.id()) {
723 case core::extrema::Sm: f += "smin."; break;
724 case core::extrema::SM: f += "smax."; break;
725 case core::extrema::sm: f += "umin."; break;
726 case core::extrema::sM: f += "umax."; break;
727 }
728 f += t;
729 declare("{} @{}({}, {})", t, f, t, t);
730 return bb.assign(name, "tail call {} @{}({} {}, {} {})", t, f, t, a, t, b);
731 } else if (auto abs = Axm::isa<core::abs>(def)) {
732 auto [m, x] = abs->args<2>();
733 auto t = convert(x->type());
734 auto a = emit(x);
735 std::string f = "llvm.abs." + t;
736 declare("{} @{}({}, {})", t, f, t, "i1");
737 return bb.assign(name, "tail call {} @{}({} {}, {} {})", t, f, t, a, "i1", "1");
738 } else if (auto conv = Axm::isa<core::conv>(def)) {
739 auto v_src = emit(conv->arg());
740 auto t_src = convert(conv->arg()->type());
741 auto t_dst = convert(conv->type());
742
743 nat_t w_src = *Idx::size2bitwidth(Idx::isa(conv->arg()->type()));
744 nat_t w_dst = *Idx::size2bitwidth(Idx::isa(conv->type()));
745
746 if (w_src == w_dst) return v_src;
747
748 switch (conv.id()) {
749 case core::conv::s: op = w_src < w_dst ? "sext" : "trunc"; break;
750 case core::conv::u: op = w_src < w_dst ? "zext" : "trunc"; break;
751 }
752
753 return bb.assign(name, "{} {} {} to {}", op, t_src, v_src, t_dst);
754 } else if (auto bitcast = Axm::isa<core::bitcast>(def)) {
755 auto dst_type_ptr = Axm::isa<mem::Ptr>(bitcast->type());
756 auto src_type_ptr = Axm::isa<mem::Ptr>(bitcast->arg()->type());
757 auto v_src = emit(bitcast->arg());
758 auto t_src = convert(bitcast->arg()->type());
759 auto t_dst = convert(bitcast->type());
760
761 if (auto lit = Lit::isa(bitcast->arg()); lit && *lit == 0) return "zeroinitializer";
762 // clang-format off
763 if (src_type_ptr && dst_type_ptr) return bb.assign(name, "bitcast {} {} to {}", t_src, v_src, t_dst);
764 if (src_type_ptr) return bb.assign(name, "ptrtoint {} {} to {}", t_src, v_src, t_dst);
765 if (dst_type_ptr) return bb.assign(name, "inttoptr {} {} to {}", t_src, v_src, t_dst);
766 // clang-format on
767
768 auto size2width = [&](const Def* type) {
769 if (type->isa<Nat>()) return 64_n;
770 if (auto size = Idx::isa(type)) return *Idx::size2bitwidth(size);
771 return 0_n;
772 };
773
774 auto src_size = size2width(bitcast->arg()->type());
775 auto dst_size = size2width(bitcast->type());
776
777 op = "bitcast";
778 if (src_size && dst_size) {
779 if (src_size == dst_size) return v_src;
780 op = (src_size < dst_size) ? "zext" : "trunc";
781 }
782 return bb.assign(name, "{} {} {} to {}", op, t_src, v_src, t_dst);
783 } else if (auto lea = Axm::isa<mem::lea>(def)) {
784 auto [ptr, i] = lea->args<2>();
785 auto pointee = Axm::as<mem::Ptr>(ptr->type())->arg(0);
786 auto v_ptr = emit(ptr);
787 auto t_pointee = convert(pointee);
788 auto t_ptr = convert(ptr->type());
789 if (pointee->isa<Sigma>())
790 return bb.assign(name, "getelementptr inbounds {}, {} {}, i64 0, i32 {}", t_pointee, t_ptr, v_ptr,
791 Lit::as(i));
792
793 assert(pointee->isa<Arr>());
794 auto [v_i, t_i] = emit_gep_index(i);
795
796 return bb.assign(name, "getelementptr inbounds {}, {} {}, i64 0, {} {}", t_pointee, t_ptr, v_ptr, t_i, v_i);
797 } else if (auto malloc = Axm::isa<mem::malloc>(def)) {
798 declare("i8* @malloc(i64)");
799
800 emit_unsafe(malloc->arg(0));
801 auto size = emit(malloc->arg(1));
802 auto ptr_t = convert(Axm::as<mem::Ptr>(def->proj(1)->type()));
803 bb.assign(name + "i8", "call i8* @malloc(i64 {})", size);
804 return bb.assign(name, "bitcast i8* {} to {}", name + "i8", ptr_t);
805 } else if (auto free = Axm::isa<mem::free>(def)) {
806 declare("void @free(i8*)");
807 emit_unsafe(free->arg(0));
808 auto ptr = emit(free->arg(1));
809 auto ptr_t = convert(Axm::as<mem::Ptr>(free->arg(1)->type()));
810
811 bb.assign(name + "i8", "bitcast {} {} to i8*", ptr_t, ptr);
812 bb.tail("call void @free(i8* {})", name + "i8");
813 return {};
814 } else if (auto mslot = Axm::isa<mem::mslot>(def)) {
815 auto [Ta, msi] = mslot->uncurry_args<2>();
816 auto [pointee, addr_space] = Ta->projs<2>();
817 auto [mem, _, __] = msi->projs<3>();
818 emit_unsafe(mslot->arg(0));
819 // TODO array with size
820 // auto v_size = emit(mslot->arg(1));
821 print(bb.body().emplace_back(), "{} = alloca {}", name, convert(pointee));
822 return name;
823 } else if (auto free = Axm::isa<mem::free>(def)) {
824 declare("void @free(i8*)");
825
826 emit_unsafe(free->arg(0));
827 auto v_ptr = emit(free->arg(1));
828 auto t_ptr = convert(Axm::as<mem::Ptr>(free->arg(1)->type()));
829
830 bb.assign(name + "i8", "bitcast {} {} to i8*", t_ptr, v_ptr);
831 bb.tail("call void @free(i8* {})", name + "i8");
832 return {};
833 } else if (auto load = Axm::isa<mem::load>(def)) {
834 emit_unsafe(load->arg(0));
835 auto v_ptr = emit(load->arg(1));
836 auto t_ptr = convert(load->arg(1)->type());
837 auto t_pointee = convert(Axm::as<mem::Ptr>(load->arg(1)->type())->arg(0));
838 return bb.assign(name, "load {}, {} {}", t_pointee, t_ptr, v_ptr);
839 } else if (auto store = Axm::isa<mem::store>(def)) {
840 emit_unsafe(store->arg(0));
841 auto v_ptr = emit(store->arg(1));
842 auto v_val = emit(store->arg(2));
843 auto t_ptr = convert(store->arg(1)->type());
844 auto t_val = convert(store->arg(2)->type());
845 print(bb.body().emplace_back(), "store {} {}, {} {}", t_val, v_val, t_ptr, v_ptr);
846 return {};
847 } else if (auto q = Axm::isa<clos::alloc_jmpbuf>(def)) {
848 declare("i64 @jmpbuf_size()");
849
850 emit_unsafe(q->arg());
851 auto size = name + ".size";
852 bb.assign(size, "call i64 @jmpbuf_size()");
853 return bb.assign(name, "alloca i8, i64 {}", size);
854 } else if (auto setjmp = Axm::isa<clos::setjmp>(def)) {
855 declare("i32 @_setjmp(i8*) returns_twice");
856
857 auto [mem, jmpbuf] = setjmp->arg()->projs<2>();
858 emit_unsafe(mem);
859 auto v_jb = emit(jmpbuf);
860 return bb.assign(name, "call i32 @_setjmp(i8* {})", v_jb);
861 } else if (auto arith = Axm::isa<math::arith>(def)) {
862 auto [mode, ab] = arith->uncurry_args<2>();
863 auto [a, b] = ab->projs<2>([this](auto def) { return emit(def); });
864 auto t = convert(arith->type());
865 auto lmode = Lit::as(mode);
866
867 switch (arith.id()) {
868 case math::arith::add: op = "fadd"; break;
869 case math::arith::sub: op = "fsub"; break;
870 case math::arith::mul: op = "fmul"; break;
871 case math::arith::div: op = "fdiv"; break;
872 case math::arith::rem: op = "frem"; break;
873 }
874
875 if (lmode == math::Mode::fast)
876 op += " fast";
877 else {
878 // clang-format off
879 if (lmode & math::Mode::nnan ) op += " nnan";
880 if (lmode & math::Mode::ninf ) op += " ninf";
881 if (lmode & math::Mode::nsz ) op += " nsz";
882 if (lmode & math::Mode::arcp ) op += " arcp";
883 if (lmode & math::Mode::contract) op += " contract";
884 if (lmode & math::Mode::afn ) op += " afn";
885 if (lmode & math::Mode::reassoc ) op += " reassoc";
886 // clang-format on
887 }
888
889 return bb.assign(name, "{} {} {}, {}", op, t, a, b);
890 } else if (auto tri = Axm::isa<math::tri>(def)) {
891 auto a = emit(tri->arg());
892 auto t = convert(tri->type());
893
894 std::string f;
895
896 if (tri.id() == math::tri::sin) {
897 f = "llvm.sin"s + llvm_suffix(tri->type());
898 } else if (tri.id() == math::tri::cos) {
899 f = "llvm.cos"s + llvm_suffix(tri->type());
900 } else {
901 if (tri.sub() & sub_t(math::tri::a)) f += "a";
902
903 switch (math::tri((tri.id() & 0x3) | Annex::base<math::tri>())) {
904 case math::tri::sin: f += "sin"; break;
905 case math::tri::cos: f += "cos"; break;
906 case math::tri::tan: f += "tan"; break;
907 case math::tri::ahFF: error("this axm is supposed to be unused");
908 default: fe::unreachable();
909 }
910
911 if (tri.sub() & sub_t(math::tri::h)) f += "h";
912 f += math_suffix(tri->type());
913 }
914
915 declare("{} @{}({})", t, f, t);
916 return bb.assign(name, "tail call {} @{}({} {})", t, f, t, a);
917 } else if (auto extrema = Axm::isa<math::extrema>(def)) {
918 auto [a, b] = extrema->args<2>([this](auto def) { return emit(def); });
919 auto t = convert(extrema->type());
920 std::string f = "llvm.";
921 switch (extrema.id()) {
922 case math::extrema::fmin: f += "minnum"; break;
923 case math::extrema::fmax: f += "maxnum"; break;
924 case math::extrema::ieee754min: f += "minimum"; break;
925 case math::extrema::ieee754max: f += "maximum"; break;
926 }
927 f += llvm_suffix(extrema->type());
928
929 declare("{} @{}({}, {})", t, f, t, t);
930 return bb.assign(name, "tail call {} @{}({} {}, {} {})", t, f, t, a, t, b);
931 } else if (auto pow = Axm::isa<math::pow>(def)) {
932 auto [a, b] = pow->args<2>([this](auto def) { return emit(def); });
933 auto t = convert(pow->type());
934 std::string f = "llvm.pow";
935 f += llvm_suffix(pow->type());
936 declare("{} @{}({}, {})", t, f, t, t);
937 return bb.assign(name, "tail call {} @{}({} {}, {} {})", t, f, t, a, t, b);
938 } else if (auto rt = Axm::isa<math::rt>(def)) {
939 auto a = emit(rt->arg());
940 auto t = convert(rt->type());
941 std::string f;
942 if (rt.id() == math::rt::sq)
943 f = "llvm.sqrt"s + llvm_suffix(rt->type());
944 else
945 f = "cbrt"s += math_suffix(rt->type());
946 declare("{} @{}({})", t, f, t);
947 return bb.assign(name, "tail call {} @{}({} {})", t, f, t, a);
948 } else if (auto exp = Axm::isa<math::exp>(def)) {
949 auto a = emit(exp->arg());
950 auto t = convert(exp->type());
951 std::string f = "llvm.";
952 f += (exp.sub() & sub_t(math::exp::log)) ? "log" : "exp";
953 f += (exp.sub() & sub_t(math::exp::bin)) ? "2" : (exp.sub() & sub_t(math::exp::dec)) ? "10" : "";
954 f += llvm_suffix(exp->type());
955 // TODO doesn't work for exp10"
956 declare("{} @{}({})", t, f, t);
957 return bb.assign(name, "tail call {} @{}({} {})", t, f, t, a);
958 } else if (auto er = Axm::isa<math::er>(def)) {
959 auto a = emit(er->arg());
960 auto t = convert(er->type());
961 auto f = er.id() == math::er::f ? "erf"s : "erfc"s;
962 f += math_suffix(er->type());
963 declare("{} @{}({})", t, f, t);
964 return bb.assign(name, "tail call {} @{}({} {})", t, f, t, a);
965 } else if (auto gamma = Axm::isa<math::gamma>(def)) {
966 auto a = emit(gamma->arg());
967 auto t = convert(gamma->type());
968 std::string f = gamma.id() == math::gamma::t ? "tgamma" : "lgamma";
969 f += math_suffix(gamma->type());
970 declare("{} @{}({})", t, f, t);
971 return bb.assign(name, "tail call {} @{}({} {})", t, f, t, a);
972 } else if (auto cmp = Axm::isa<math::cmp>(def)) {
973 auto [a, b] = cmp->args<2>([this](auto def) { return emit(def); });
974 auto t = convert(cmp->arg(0)->type());
975 op = "fcmp ";
976
977 switch (cmp.id()) {
978 // clang-format off
979 case math::cmp:: e: op += "oeq"; break;
980 case math::cmp:: l: op += "olt"; break;
981 case math::cmp:: le: op += "ole"; break;
982 case math::cmp:: g: op += "ogt"; break;
983 case math::cmp:: ge: op += "oge"; break;
984 case math::cmp:: ne: op += "one"; break;
985 case math::cmp:: o: op += "ord"; break;
986 case math::cmp:: u: op += "uno"; break;
987 case math::cmp:: ue: op += "ueq"; break;
988 case math::cmp:: ul: op += "ult"; break;
989 case math::cmp::ule: op += "ule"; break;
990 case math::cmp:: ug: op += "ugt"; break;
991 case math::cmp::uge: op += "uge"; break;
992 case math::cmp::une: op += "une"; break;
993 // clang-format on
994 default: fe::unreachable();
995 }
996
997 return bb.assign(name, "{} {} {}, {}", op, t, a, b);
998 } else if (auto conv = Axm::isa<math::conv>(def)) {
999 auto v_src = emit(conv->arg());
1000 auto t_src = convert(conv->arg()->type());
1001 auto t_dst = convert(conv->type());
1002
1003 auto s_src = math::isa_f(conv->arg()->type());
1004 auto s_dst = math::isa_f(conv->type());
1005
1006 switch (conv.id()) {
1007 case math::conv::f2f: op = s_src < s_dst ? "fpext" : "fptrunc"; break;
1008 case math::conv::s2f: op = "sitofp"; break;
1009 case math::conv::u2f: op = "uitofp"; break;
1010 case math::conv::f2s: op = "fptosi"; break;
1011 case math::conv::f2u: op = "fptoui"; break;
1012 }
1013
1014 return bb.assign(name, "{} {} {} to {}", op, t_src, v_src, t_dst);
1015 } else if (auto abs = Axm::isa<math::abs>(def)) {
1016 auto a = emit(abs->arg());
1017 auto t = convert(abs->type());
1018 std::string f = "llvm.fabs";
1019 f += llvm_suffix(abs->type());
1020 declare("{} @{}({})", t, f, t);
1021 return bb.assign(name, "tail call {} @{}({} {})", t, f, t, a);
1022 } else if (auto round = Axm::isa<math::round>(def)) {
1023 auto a = emit(round->arg());
1024 auto t = convert(round->type());
1025 std::string f = "llvm.";
1026 switch (round.id()) {
1027 case math::round::f: f += "floor"; break;
1028 case math::round::c: f += "ceil"; break;
1029 case math::round::r: f += "round"; break;
1030 case math::round::t: f += "trunc"; break;
1031 }
1032 f += llvm_suffix(round->type());
1033 declare("{} @{}({})", t, f, t);
1034 return bb.assign(name, "tail call {} @{}({} {})", t, f, t, a);
1035 }
1036 error("unhandled def in LLVM backend: {} : {}", def, def->type());
1037}
1038
1039void emit(World& world, std::ostream& ostream) {
1040 Emitter emitter(world, ostream);
1041 emitter.run();
1042}
1043
1044int compile(World& world, std::string name) {
1045#ifdef _WIN32
1046 auto exe = name + ".exe"s;
1047#else
1048 auto exe = name;
1049#endif
1050 return compile(world, name + ".ll"s, exe);
1051}
1052
1053int compile(World& world, std::string ll, std::string out) {
1054 std::ofstream ofs(ll);
1055 emit(world, ofs);
1056 ofs.close();
1057 auto cmd = fmt("clang \"{}\" -o \"{}\" -Wno-override-module", ll, out);
1058 return sys::system(cmd);
1059}
1060
1061int compile_and_run(World& world, std::string name, std::string args) {
1062 if (compile(world, name) == 0) return sys::run(name, args);
1063 error("compilation failed");
1064}
1065
1066} // namespace mim::ll
A (possibly paramterized) Array.
Definition tuple.h:117
static auto isa(const Def *def)
Definition axm.h:107
static auto as(const Def *def)
Definition axm.h:129
Matches (ff, tt)#cond arg where cond is not a Literal.
Definition tuple.h:279
Lam * root() const
Definition phase.h:304
Base class for all Defs.
Definition def.h:251
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.
Definition def.cpp:587
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:486
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:390
nat_t num_vars() noexcept
Definition def.h:429
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.cpp:446
auto vars(F f) noexcept
Definition def.h:429
std::string unique_name() const
name + "_" + Def::gid
Definition def.cpp:578
Matches a dispatch through a jump table of the form: (target_0, target_1, ...)#index arg where index ...
Definition tuple.h:304
Extracts from a Sigma or Array-typed Extract::tuple the element at position Extract::index.
Definition tuple.h:206
static constexpr nat_t size2bitwidth(nat_t n)
Definition def.h:893
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
Definition def.cpp:610
Creates a new Tuple / Pack by inserting Insert::value at position Insert::index into Insert::tuple.
Definition tuple.h:233
A function.
Definition lam.h:110
static Lam * isa_mut_basicblock(const Def *d)
Only for mutables.
Definition lam.h:145
const Pi * type() const
Definition lam.h:130
const Def * body() const
Definition lam.h:123
static std::optional< T > isa(const Def *def)
Definition def.h:826
static T as(const Def *def)
Definition def.h:832
const Nest & nest() const
Definition phase.h:320
A (possibly paramterized) Tuple.
Definition tuple.h:166
virtual void run()
Entry point and generates some debug output; invokes Phase::start.
Definition phase.cpp:13
virtual void start()=0
Actual entry.
A dependent function type.
Definition lam.h:14
const Pi * ret_pi() const
Yields the last Pi::dom, if Pi::isa_basicblock.
Definition lam.cpp:13
static const Pi * isa_returning(const Def *d)
Is this a continuation (Pi::isa_cn) which has a Pi::ret_pi?
Definition lam.h:49
static Schedule schedule(const Nest &)
Definition schedule.cpp:118
Matches (ff, tt)#cond - where cond is not a Literal.
Definition tuple.h:261
A dependent tuple type.
Definition tuple.h:20
World & world()
Definition pass.h:64
std::string_view name() const
Definition pass.h:67
Data constructor for a Sigma.
Definition tuple.h:68
A variable introduced by a binder (mutable).
Definition def.h:702
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:31
void emit_imported(Lam *)
Definition ll.cpp:244
Emitter(World &world, std::ostream &ostream)
Definition ll.cpp:106
std::string prepare()
Definition ll.cpp:258
void finalize()
Definition ll.cpp:274
void start() override
Actual entry.
Definition ll.cpp:233
std::string emit_bb(BB &, const Def *)
Definition ll.cpp:437
bool is_valid(std::string_view s)
Definition ll.cpp:109
mim::Emitter< std::string, std::string, BB, Emitter > Super
Definition ll.cpp:104
void emit_epilogue(Lam *)
Definition ll.cpp:303
void declare(const char *s, Args &&... args)
Definition ll.cpp:118
Definition ll.h:9
int compile_and_run(World &, std::string name, std::string args={})
Definition ll.cpp:1061
void emit(World &, std::ostream &)
Definition ll.cpp:1039
int compile(World &, std::string name)
Definition ll.cpp:1044
The clos Plugin
Definition clos.h:7
The core Plugin
Definition core.h:8
The math Plugin
Definition math.h:8
The mem Plugin
Definition mem.h:11
int run(std::string cmd, std::string args={})
Wraps sys::system and puts .exe at the back (Windows) and ./ at the front (otherwise) of cmd.
Definition sys.cpp:77
int system(std::string)
Wraps std::system and makes the return value usable.
Definition sys.cpp:71
u64 nat_t
Definition types.h:44
Vector< const Def * > DefVec
Definition def.h:77
u8 sub_t
Definition types.h:49
D bitcast(const S &src)
A bitcast from src of type S to D.
Definition util.h:23
std::ostream & print(std::ostream &os, const char *s)
Base case.
Definition print.cpp:5
double f64
Definition types.h:42
std::string fmt(const char *s, Args &&... args)
Wraps mim::print to output a formatted std:string.
Definition print.h:163
void error(Loc loc, const char *f, Args &&... args)
Definition dbg.h:125
float f32
Definition types.h:41
GIDMap< const Def *, To > DefMap
Definition def.h:73
TExt< true > Top
Definition lattice.h:172
TExt< false > Bot
Definition lattice.h:171
uint64_t u64
Definition types.h:35
@ Nat
Definition def.h:114
@ Pi
Definition def.h:114
@ Arr
Definition def.h:114
@ Sigma
Definition def.h:114
uint16_t u16
Definition types.h:35
static consteval flags_t base()
Definition plugin.h:119
std::deque< std::ostringstream > & tail()
Definition ll.cpp:79
void tail(const char *s, Args &&... args)
Definition ll.cpp:88
DefMap< std::deque< std::pair< std::string, std::string > > > phis
Definition ll.cpp:98
BB(const BB &)=delete
BB()=default
std::deque< std::ostringstream > & body()
Definition ll.cpp:78
BB(BB &&other) noexcept=default
friend void swap(BB &a, BB &b) noexcept
Definition ll.cpp:92
std::array< std::deque< std::ostringstream >, 3 > parts
Definition ll.cpp:99
std::deque< std::ostringstream > & head()
Definition ll.cpp:77
BB & operator=(BB other) noexcept
Definition ll.cpp:75
std::string assign(std::string_view name, const char *s, Args &&... args)
Definition ll.cpp:82