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