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