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