17using namespace std::string_literals;
19namespace clos = mim::plug::clos;
20namespace core = mim::plug::core;
21namespace vec = mim::plug::vec;
34 auto path =
name +
".ll"s;
36 for (
const auto& arg :
args()) {
37 world().DLOG(
"ll backend arg: `{}`", arg);
38 if (arg.starts_with(
"o="))
40 else if (arg.starts_with(
"output="))
42 else if (arg ==
"rt=embed")
44 else if (arg ==
"rt=extern")
47 auto ofs = std::ofstream(path);
62std::string Emitter::convert_impl(
const Def* type,
bool simd) {
63 if (
auto i =
types_.find(type); i !=
types_.end())
return i->second;
65 if (
Axm::isa<mem::M>(type)) fe::throwf(
"ll backend: cannot convert %mem.M type '{}'", type);
69 if (type->isa<
Nat>()) {
70 return types_[type] =
"i64";
75 case 16:
return types_[type] =
"half";
76 case 32:
return types_[type] =
"float";
77 case 64:
return types_[type] =
"double";
78 default: fe::throwf(
"ll backend: unsupported floating-point width {} in type '{}'", *w, type);
81 auto [
pointee, addr_space] = ptr->args<2>();
82 std::print(s,
"{} addrspace({})*",
convert(pointee,
false), addr_space);
83 }
else if (
auto arr =
type->isa<
Arr>()) {
84 if (
auto se =
is_simd(arr); se && simd) {
85 auto [
size, elem] = *se;
86 std::print(s,
"<{} x {}>", size,
convert(elem));
90 std::print(s,
"[{} x {}]", size,
convert(arr->body(),
false));
92 }
else if (
auto pi =
type->isa<
Pi>()) {
93 if (!
Pi::isa_returning(pi)) fe::throwf(
"ll backend: cannot convert the type of a basic block: '{}'", pi);
96 if (
auto t = detail::isa_mem_sigma_2(pi->dom()))
99 auto doms = pi->doms();
100 for (
auto sep =
"";
auto dom : doms.view().rsubspan(1)) {
107 }
else if (
auto t = detail::isa_mem_sigma_2(type)) {
109 }
else if (
auto sigma =
type->isa<
Sigma>()) {
110 if (sigma->isa_mut()) {
113 std::print(s,
"{} = type",
name);
117 for (
auto sep =
"";
auto t : sigma->ops()) {
124 fe::throwf(
"ll backend: cannot convert type '{}' to LLVM", type);
129 if (
s.str().empty()) fe::throwf(
"ll backend: empty type declaration for '{}'", type);
134void Emitter::finalize_impl() {
135 for (
auto& [lam, bb] :
lam2bb_) {
136 for (
const auto& [phi,
args] : bb.phis) {
137 std::print(bb.head().emplace_back(),
"{} = phi {} ",
id(phi),
convert(phi->type()));
138 for (
auto sep =
"";
const auto& [arg, pred] :
args) {
139 std::print(bb.head().back(),
"{}[ {}, {} ]", sep, arg, pred);
146 if (
auto lam = mut->isa_mut<
Lam>()) {
147 if (!
lam2bb_.contains(lam)) fe::throwf(
"ll backend: no basic block was emitted for '{}'", lam);
149 std::print(
func_impls_,
"{}:\n", lam->unique_name());
152 for (
const auto& part : bb.parts)
153 for (
const auto& line : part)
180void Emitter::emit_epilogue_impl(
Lam* lam) {
181 auto app = lam->
body()->expect<
App>(
"an application in tail position");
186 if (app->callee() ==
root()->ret_var()) {
189 for (
auto arg : app->args()) {
191 values.emplace_back(val);
192 types.emplace_back(arg->type());
196 switch (values.size()) {
197 case 0:
return bb.tail(
"ret void");
200 : bb.tail(
"ret {} {}",
convert(types[0]), values[0]);
208 auto v_src =
emit(common_src);
209 auto t =
convert(common_src->type());
210 return bb.tail(
"ret {} {}", t, v_src);
212 auto [
size, elem] = *se;
215 type = std::format(
"<{} x {}>", size, val_t);
216 for (
auto val : values) {
221 prev += std::format(
"{} {}", val_t, val);
227 for (
size_t i = 0, n = values.size(); i != n; ++i) {
229 auto v_elem = values[i];
230 auto t_elem =
convert(types[i]);
231 auto namei =
"%ret_val." + std::to_string(i);
232 bb.tail(
"{} = insertvalue {} {}, {} {}, {}", namei, type, prev, t_elem, v_elem, i);
236 bb.tail(
"ret {} {}", type, prev);
240 }
else if (
auto dispatch = Dispatch(app)) {
241 for (
auto callee : dispatch.tuple()->projs([](
const Def* def) { return def->isa_mut<Lam>(); }))
242 if (
size_t n = callee->num_tvars(); n == 1 &&
is_simd(callee->var(0)->type()))
243 emit_phi(callee, callee->var(0),
emit(app->arg(n, 0)), lam);
247 auto v_index =
emit(dispatch.index());
248 size_t n = dispatch.num_targets();
249 auto bbs = absl::FixedArray<std::string>(n);
250 for (
size_t i = 0; i != n; ++i)
251 bbs[i] =
emit(dispatch.target(i));
253 if (
auto branch = Branch(app))
return bb.tail(
"br i1 {}, label {}, label {}", v_index, bbs[1], bbs[0]);
255 auto t_index =
convert(dispatch.index()->type());
256 bb.tail(
"switch {} {}, label {} [ ", t_index, v_index, bbs[0]);
257 for (
size_t i = 1; i != n; ++i)
258 std::print(bb.tail().back(),
"{} {}, label {} ", t_index, std::to_string(i), bbs[i]);
259 std::print(bb.tail().back(),
"]");
260 }
else if (app->callee()->isa<
Bot>()) {
261 return bb.tail(
"ret ; bottom: unreachable");
265 auto v_src =
emit(common_src);
266 auto callee_var = callee->var();
270 for (
auto var : callee->vars())
276 return bb.tail(
"br label {}",
id(callee));
279 declare(
"void @longjmp(i8*, i32) noreturn");
281 auto [mem, jbuf, tag] = app->args<3>();
283 auto v_jb =
emit(jbuf);
284 auto v_tag =
emit(tag);
285 bb.tail(
"call void @longjmp(i8* {}, i32 {})", v_jb, v_tag);
286 return bb.tail(
"unreachable");
289 auto [Ta, rest] =
mslot->uncurry_args<2>();
290 auto [
pointee, addr_space] = Ta->projs<2>();
291 auto [msize, ret] = rest->projs<2>();
294 auto ret_lam = ret->expect_mut<
Lam>(
"a %mem.slot continuation");
295 auto ptr = ret_lam->var(2, 1);
296 auto v_ptr =
emit_slot(bb, app, pointee, addr_space);
298 return bb.tail(
"br label {}",
id(ret_lam));
300 auto v_callee =
emit(app->callee());
303 auto app_args = app->args();
304 for (
auto arg : app_args.view().rsubspan(1))
307 if (app->args().back()->isa<
Bot>()) {
310 fe::throwf(
"ll backend: call with a ⊥ return continuation must return void, but '{}' does not", app);
311 bb.tail(
"call void {}({})", v_callee, fe::Join(
args));
312 return bb.tail(
"unreachable");
315 auto ret_lam = app->args().back()->expect_mut<
Lam>(
"a return continuation");
317 for (
auto var : ret_lam->vars())
321 bb.tail(
"call void {}({})", v_callee, fe::Join(
args));
323 auto name =
"%" + app->unique_name() +
"ret";
325 bb.tail(
"{} = call {} {}({})",
name, t_ret, v_callee, fe::Join(
args));
329 return bb.tail(
"br label {}",
id(ret_lam));
333std::string Emitter::emit_bb_impl(
BB& bb,
const Def* def) {
334 if (
auto lam = def->isa<
Lam>())
return id(lam);
339 auto emit_tuple = [&](
const Def* tuple) {
340 if (detail::isa_mem_sigma_2(tuple->type())) {
342 return emit(tuple->proj(2, 1));
345 if (tuple->is_closed()) {
346 bool is_array = tuple->type()->isa<
Arr>();
347 auto simd_array =
convert(tuple->type()).front() ==
'<';
349 s += simd_array ?
"<" : is_array ?
"[" :
"{";
351 for (
size_t i = 0, n = tuple->num_projs(); i != n; ++i) {
352 auto e = tuple->proj(n, i);
353 if (
auto v_elem =
emit_unsafe(e); !v_elem.empty()) {
355 s += sep + t_elem +
" " + v_elem;
360 return s += simd_array ?
">" : is_array ?
"]" :
"}";
363 std::string prev =
"undef";
365 for (
size_t src = 0, dst = 0, n = tuple->num_projs(); src != n; ++src) {
366 auto e = tuple->proj(n, src);
370 auto namei =
name +
"." + std::to_string(dst);
371 if (
t.front() ==
'<')
372 prev = bb.assign(namei,
"insertelement {} {}, {} {}, {} {}", t, prev, elem_t, elem, elem_t, dst);
374 prev = bb.assign(namei,
"insertvalue {} {}, {} {}, {}", t, prev, elem_t, elem, dst);
381 if (def->isa<
Var>()) {
384 if (std::ranges::any_of(ts, [](
auto t) {
return Axm::isa<mem::M>(t); }))
return {};
385 return emit_tuple(def);
388 auto emit_gep_index = [&](
const Def*
index) {
389 auto v_i =
emit(index);
394 v_i = bb.assign(
name +
".zext",
395 "zext {} {} to i{} ; add one more bit for gep index as it is treated as signed value",
397 t_i =
"i" + std::to_string(*w + 1);
401 return std::pair(v_i, t_i);
404 if (
auto lit = def->isa<
Lit>()) {
406 return std::to_string(
lit->get());
413 s <<
"0xH" << std::setfill(
'0') << std::setw(4) << std::right << std::hex <<
lit->get<
u16>();
416 hex = std::bit_cast<u64>(
f64(
lit->get<
f32>()));
419 case 64: hex =
lit->get<
u64>();
break;
420 default: fe::throwf(
"ll backend: unsupported floating-point width {} for literal '{}'", *w, def);
423 s <<
"0x" << std::setfill(
'0') << std::setw(16) << std::right << std::hex << hex;
426 fe::throwf(
"ll backend: cannot emit literal '{}' of type '{}'", def, def->
type());
427 }
else if (def->isa<
Bot>()) {
429 }
else if (
auto top = def->isa<
Top>()) {
432 }
else if (
auto tuple = def->isa<
Tuple>()) {
433 return emit_tuple(tuple);
434 }
else if (
auto pack = def->isa<
Pack>()) {
435 if (
auto lit =
Lit::isa(pack->body()); lit && *lit == 0)
return "zeroinitializer";
436 return emit_tuple(pack);
437 }
else if (
auto sel = Select(def)) {
438 auto t =
convert(sel.extract()->type());
439 auto [elem_a, elem_b] = sel.pair()->projs<2>([&](
auto e) {
return emit_unsafe(e); });
440 auto cond_t =
convert(sel.cond()->type());
442 return bb.assign(
name,
"select {} {}, {} {}, {} {}", cond_t, cond, t, elem_b, t, elem_a);
443 }
else if (
auto extract = def->isa<
Extract>()) {
444 auto tuple = extract->tuple();
445 auto index = extract->index();
452 if (
auto sigma = extract->type()->isa<
Sigma>(); sigma && sigma->num_ops() == 0)
return {};
454 auto t_tup =
convert(tuple->type());
456 if (detail::isa_mem_sigma_2(tuple->type()))
return v_tup;
460 if (
auto sigma = tuple->type()->isa<
Sigma>())
461 for (
u64 i = 0; i < *li; ++i)
464 return bb.assign(
name,
"extractvalue {} {}, {}", t_tup, v_tup, v_i);
467 auto t_elem =
convert(extract->type());
468 auto [v_i, t_i] = emit_gep_index(index);
471 "{}.alloca = alloca {} ; copy to alloca to emulate extract with store + gep + load",
name, t_tup);
472 std::print(bb.body().emplace_back(),
"store {} {}, {}* {}.alloca", t_tup, v_tup, t_tup,
name);
473 std::print(bb.body().emplace_back(),
"{}.gep = getelementptr inbounds {}, {}* {}.alloca, i64 0, {} {}",
name,
474 t_tup, t_tup,
name, t_i, v_i);
475 return bb.assign(
name,
"load {}, {}* {}.gep", t_elem, t_elem,
name);
476 }
else if (
auto insert = def->isa<
Insert>()) {
478 fe::throwf(
"ll backend: cannot insert into a tuple with a %mem.M element: '{}'", insert);
487 return bb.assign(
name,
"insertelement {} {}, {} {}, i32 {}", t_tup, v_tup, t_val, v_val, v_idx);
490 return bb.assign(
name,
" insertvalue {} {}, {} {}, {}", t_tup, v_tup, t_val, v_val, v_idx);
497 v_i = bb.assign(
name +
".idx",
"{} {} {} to i32", w_src < 32 ?
"zext" :
"trunc", t_i, v_i);
499 return bb.assign(
name,
"insertelement {} {}, {} {}, i32 {}", t_tup, v_tup, t_val, v_val, v_i);
502 auto [v_i, t_i] = emit_gep_index(
insert->index());
504 "{}.alloca = alloca {} ; copy to alloca to emulate insert with store + gep + load",
name, t_tup);
505 std::print(bb.body().emplace_back(),
"store {} {}, {}* {}.alloca", t_tup, v_tup, t_tup,
name);
506 std::print(bb.body().emplace_back(),
"{}.gep = getelementptr inbounds {}, {}* {}.alloca, i64 0, {} {}",
507 name, t_tup, t_tup,
name, t_i, v_i);
508 std::print(bb.body().emplace_back(),
"store {} {}, {}* {}.gep", t_val, v_val, t_val,
name);
509 return bb.assign(
name,
"load {}, {}* {}.alloca", t_tup, t_tup,
name);
511 }
else if (
auto global = def->isa<
Global>()) {
512 auto v_init =
emit(global->init());
517 auto [
a, b] =
nat->args<2>([
this](
auto def) {
return emit(def); });
523 auto ugt = bb.assign(
name +
".ugt",
"icmp ugt i64 {}, {}", b, a);
524 auto raw = bb.assign(
name +
".raw",
"sub i64 {}, {}", a, b);
525 return bb.assign(
name,
"select i1 {}, i64 0, i64 {}", ugt, raw);
532 auto bz = bb.assign(
name +
".bz",
"icmp eq i64 {}, 0", b);
533 auto bsaf = bb.assign(
name +
".bsafe",
"select i1 {}, i64 1, i64 {}", bz, b);
534 auto q = bb.assign(
name +
".q",
"udiv i64 {}, {}", a, bsaf);
535 return bb.assign(
name,
"select i1 {}, i64 0, i64 {}", bz, q);
538 auto bz = bb.assign(
name +
".bz",
"icmp eq i64 {}, 0", b);
539 auto bsaf = bb.assign(
name +
".bsafe",
"select i1 {}, i64 1, i64 {}", bz, b);
540 auto r = bb.assign(
name +
".r",
"urem i64 {}, {}", a, bsaf);
541 return bb.assign(
name,
"select i1 {}, i64 {}, i64 {}", bz, a, r);
545 auto [
a, b] =
ncmp->args<2>([
this](
auto def) {
return emit(def); });
557 default: fe::throwf(
"ll backend: unhandled %core.ncmp id in '{}'", def);
560 return bb.assign(
name,
"{} i64 {}, {}", op, a, b);
565 if (s < 64)
return bb.assign(
name,
"trunc i64 {} to {}", x, t);
568 if (
bit1.id() !=
core::bit1::neg) fe::throwf(
"ll backend: unhandled %core.bit1 id in '{}'", def);
571 return bb.assign(
name,
"xor {} -1, {}", t, x);
573 auto [
a, b] =
bit2->args<2>([
this](
auto def) {
return emit(def); });
576 auto neg = [&](std::string_view x) {
return bb.assign(
name +
".neg",
"xor {} -1, {}", t, x); };
589 default: fe::throwf(
"ll backend: unhandled %core.bit2 id in '{}'", def);
592 auto [
a, b] =
shr->args<2>([
this](
auto def) {
return emit(def); });
600 return bb.assign(
name,
"{} {} {}, {}", op, t, a, b);
602 auto [
mode, ab] =
wrap->uncurry_args<2>();
603 auto [
a, b] = ab->projs<2>([
this](
auto def) {
return emit(def); });
617 return bb.assign(
name,
"{} {} {}, {}", op, t, a, b);
619 auto [m, xy] =
div->args<2>();
620 auto [x, y] = xy->projs<2>();
633 return bb.assign(
name,
"{} {} {}, {}", op, t, a, b);
635 auto [
a, b] =
icmp->args<2>([
this](
auto def) {
return emit(def); });
652 default: fe::throwf(
"ll backend: unhandled %core.icmp id in '{}'", def);
655 return bb.assign(
name,
"{} {} {}, {}", op, t, a, b);
657 auto [x, y] = extr->args<2>();
661 std::string
f =
"llvm.";
669 declare(
"{} @{}({}, {})", t, f, t, t);
670 return bb.assign(
name,
"tail call {} @{}({} {}, {} {})", t, f, t, a, t, b);
672 auto [m, x] =
abs->args<2>();
675 std::string
f =
"llvm.abs." +
t;
676 declare(
"{} @{}({}, {})", t, f, t,
"i1");
677 return bb.assign(
name,
"tail call {} @{}({} {}, {} {})", t, f, t, a,
"i1",
"1");
686 if (w_src == w_dst)
return v_src;
693 return bb.assign(
name,
"{} {} {} to {}", op, t_src, v_src, t_dst);
701 if (
auto lit =
Lit::isa(
bitcast->arg()); lit && *lit == 0)
return "zeroinitializer";
703 if (src_type_ptr && dst_type_ptr)
return bb.assign(
name,
"bitcast {} {} to {}", t_src, v_src, t_dst);
704 if (src_type_ptr)
return bb.assign(
name,
"ptrtoint {} {} to {}", t_src, v_src, t_dst);
705 if (dst_type_ptr)
return bb.assign(
name,
"inttoptr {} {} to {}", t_src, v_src, t_dst);
708 auto size2width = [&](
const Def*
type) {
709 if (
type->isa<
Nat>())
return 64_n;
714 auto src_size = size2width(
bitcast->arg()->type());
715 auto dst_size = size2width(
bitcast->type());
718 if (src_size && dst_size) {
719 if (src_size == dst_size)
return v_src;
720 op = (src_size < dst_size) ?
"zext" :
"trunc";
722 return bb.assign(
name,
"{} {} {} to {}", op, t_src, v_src, t_dst);
724 auto [ptr, i] =
lea->args<2>();
726 auto v_ptr =
emit(ptr);
727 auto t_pointee =
convert(pointee);
730 return bb.assign(
name,
"getelementptr inbounds {}, {} {}, i64 0, i32 {}", t_pointee, t_ptr, v_ptr,
733 if (!
pointee->isa<
Arr>()) fe::throwf(
"ll backend: %mem.lea on a pointer to a non-aggregate '{}'", pointee);
734 auto [v_i, t_i] = emit_gep_index(i);
736 return bb.assign(
name,
"getelementptr inbounds {}, {} {}, i64 0, {} {}", t_pointee, t_ptr, v_ptr, t_i, v_i);
738 auto address_space =
malloc->decurry()->arg(1);
739 if (
Lit::expect(address_space,
"an address space") != 0)
747 auto i8ptr = bb.assign(
name +
"i8",
"call i8* @malloc(i64 {})", size);
748 std::string i8ptr_t =
"i8*";
749 if (
Lit::expect(address_space,
"an address space") != 0) {
750 i8ptr_t = std::format(
"i8 addrspace({})*", address_space);
751 i8ptr = bb.assign(
name +
"i8conv",
"addrspacecast i8* {} to {}", i8ptr, i8ptr_t);
753 return bb.assign(
name,
"bitcast {} {} to {}", i8ptr_t, i8ptr, ptr_t);
755 auto address_space =
free->decurry()->arg(1);
756 if (
Lit::expect(address_space,
"an address space") != 0)
764 auto i8ptr = bb.assign(
name +
"i8",
"bitcast {} {} to i8 addrspace({})*", ptr_t, ptr, address_space);
765 if (
Lit::expect(address_space,
"an address space") != 0)
766 i8ptr = bb.assign(
name +
"i8conv",
"addrspacecast i8 addrspace({})* {} to i8*", address_space, i8ptr);
767 bb.tail(
"call void @free(i8* {})", i8ptr);
774 return bb.assign(
name,
"load {}, {} {}", t_pointee, t_ptr, v_ptr);
781 std::print(bb.body().emplace_back(),
"store {} {}, {} {}", t_val, v_val, t_ptr, v_ptr);
790 bb.assign(size,
"call i64 @mim_jmpbuf_size()");
791 return bb.assign(
name,
"alloca i8, i64 {}", size);
793 declare(
"i32 @_setjmp(i8*) returns_twice");
795 auto [mem, jmpbuf] =
setjmp->arg()->projs<2>();
797 auto v_jb =
emit(jmpbuf);
798 return bb.assign(
name,
"call i32 @_setjmp(i8* {})", v_jb);
800 auto [
mode, ab] =
arith->uncurry_args<2>();
801 auto [
a, b] = ab->projs<2>([
this](
auto def) {
return emit(def); });
805 switch (
arith.id()) {
827 return bb.assign(
name,
"{} {} {}, {}", op, t, a, b);
835 f = std::string(
"llvm.sin") + detail::llvm_suffix(
tri->type());
837 f = std::string(
"llvm.cos") + detail::llvm_suffix(
tri->type());
846 default: fe::throwf(
"ll backend: unhandled %math.tri id in '{}'", def);
850 f += detail::math_suffix(
tri->type());
853 declare(
"{} @{}({})", t, f, t);
854 return bb.assign(
name,
"tail call {} @{}({} {})", t, f, t, a);
856 auto [
a, b] =
extrema->args<2>([
this](
auto def) {
return emit(def); });
858 std::string
f =
"llvm.";
865 f += detail::llvm_suffix(
extrema->type());
867 declare(
"{} @{}({}, {})", t, f, t, t);
868 return bb.assign(
name,
"tail call {} @{}({} {}, {} {})", t, f, t, a, t, b);
870 auto [
a, b] =
pow->args<2>([
this](
auto def) {
return emit(def); });
872 std::string
f =
"llvm.pow";
873 f += detail::llvm_suffix(
pow->type());
874 declare(
"{} @{}({}, {})", t, f, t, t);
875 return bb.assign(
name,
"tail call {} @{}({} {}, {} {})", t, f, t, a, t, b);
881 f = std::string(
"llvm.sqrt") + detail::llvm_suffix(
rt->type());
883 f = std::string(
"cbrt") += detail::math_suffix(
rt->type());
884 declare(
"{} @{}({})", t, f, t);
885 return bb.assign(
name,
"tail call {} @{}({} {})", t, f, t, a);
889 std::string
f =
"llvm.";
892 f += detail::llvm_suffix(
exp->type());
894 declare(
"{} @{}({})", t, f, t);
895 return bb.assign(
name,
"tail call {} @{}({} {})", t, f, t, a);
899 auto f =
er.id() ==
math::er::f ? std::string(
"erf") : std::string(
"erfc");
900 f += detail::math_suffix(
er->type());
901 declare(
"{} @{}({})", t, f, t);
902 return bb.assign(
name,
"tail call {} @{}({} {})", t, f, t, a);
907 f += detail::math_suffix(
gamma->type());
908 declare(
"{} @{}({})", t, f, t);
909 return bb.assign(
name,
"tail call {} @{}({} {})", t, f, t, a);
911 auto [
a, b] =
cmp->args<2>([
this](
auto def) {
return emit(def); });
917 case math::cmp:: e:
op +=
"oeq";
break;
918 case math::cmp:: l:
op +=
"olt";
break;
920 case math::cmp:: g:
op +=
"ogt";
break;
923 case math::cmp:: o:
op +=
"ord";
break;
924 case math::cmp:: u:
op +=
"uno";
break;
932 default: fe::throwf(
"ll backend: unhandled %math.cmp id in '{}'", def);
935 return bb.assign(
name,
"{} {} {}, {}", op, t, a, b);
943 auto s = detail::llvm_suffix(
is_finite->arg()->type());
944 auto f =
"llvm.is.fpclass";
945 declare(
"{} @{}{}({}, i32)", t, f, s, at);
946 return bb.assign(
name,
"tail call {} @{}{}({} {}, i32 504)", t, f, s, at, a);
963 return bb.assign(
name,
"{} {} {} to {}", op, t_src, v_src, t_dst);
967 std::string
f =
"llvm.fabs";
968 f += detail::llvm_suffix(
abs->type());
969 declare(
"{} @{}({})", t, f, t);
970 return bb.assign(
name,
"tail call {} @{}({} {})", t, f, t, a);
974 std::string
f =
"llvm.";
975 switch (
round.id()) {
981 f += detail::llvm_suffix(
round->type());
982 declare(
"{} @{}({})", t, f, t);
983 return bb.assign(
name,
"tail call {} @{}({} {})", t, f, t, a);
985 auto ni_n =
zip->decurry()->decurry()->decurry()->arg();
986 auto nat_ni =
Lit::expect(ni_n->proj(2, 0),
"a %vec.zip lane count");
987 auto f =
zip->decurry()->arg();
988 auto inputs =
zip->arg();
989 auto t_in =
convert(inputs->proj(nat_ni, 0)->type());
996 switch (nat_op.id()) {
1000 auto v1 =
emit(inputs->proj(nat_ni, 0));
1001 auto v2 =
emit(inputs->proj(nat_ni, 1));
1002 auto ugt = bb.assign(
name +
".ugt",
"icmp ugt {} {}, {}", t_in, v2, v1);
1003 auto raw = bb.assign(
name +
".raw",
"sub {} {}, {}", t_in, v1, v2);
1004 return prev = bb.assign(
name,
"select <{} x i1> {}, {} zeroinitializer, {} {}", nat_ni, ugt, t_out,
1013 Lit::expect(
f->expect<
App>(
"a zipped %math.arith")->arg(),
"a %math.arith mode"));
1014 switch (arith_op.id()) {
1035 switch (ncmp_op.id()) {
1042 default: fe::throwf(
"ll backend: unhandled zipped %core.ncmp id in '{}'", def);
1046 switch (icmp_op.id()) {
1057 default: fe::throwf(
"ll backend: unhandled zipped %core.icmp id in '{}'", def);
1061 switch (mcmp_op.id()) {
1076 default: fe::throwf(
"ll backend: unhandled zipped %math.cmp id in '{}'", def);
1079 fe::throwf(
"unhandled vec.zip operation: {}", f);
1082 auto v1 =
emit(inputs->proj(nat_ni, 0));
1083 auto v2 =
emit(inputs->proj(nat_ni, 1));
1084 prev = bb.assign(
name,
"{} {} {}, {}", op, t_in, v1, v2);
1089 fe::throwf(
"unhandled def in LLVM backend: {} : {}", def, def->
type());
1094 res = e.convert_impl(type, simd);
void reg_phases(Flags2Phases &phases)
static auto isa(const Def *def)
static auto expect(const Def *def, std::format_string< Args... > fmt, Args &&... args)
Like Axm::as but - instead of merely asserting in Debug builds - throws a formatted mim::error when d...
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.
auto projs(F f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
DefMap< std::string > locals_
std::string emit(const Def *def)
std::string emit_unsafe(const Def *def)
DefMap< std::string > globals_
DefMap< std::string > types_
static constexpr nat_t size2bitwidth(nat_t n)
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
static nat_t expect_bitwidth(const Def *type, std::format_string< Args... > fmt, Args &&... args)
Yields the bit width of the Idx type or throws a formatted mim::error - instead of yielding std::null...
static Lam * isa_mut_basicblock(const Def *d)
Only for mutables.
static std::optional< T > isa(const Def *def)
static T expect(const Def *def, std::format_string< Args... > fmt, Args &&... args)
Like Lit::as but throws a formatted mim::error instead of merely asserting in Debug; see Def::expect.
const Nest & nest() const
static void hook(Flags2Phases &phases)
Phase(World &world, std::string name)
std::string_view name() const
const Vector< std::string > & args()
Command-line arguments passed to this Phase's plugin via -X <plugin>:<arg>.
static const Pi * isa_returning(const Def *d)
Is this a continuation (Pi::isa_cn) which has a Pi::ret_pi?
static Schedule schedule(const Nest &)
The World represents the whole program and manages creation of MimIR nodes (Defs).
void start() override
Actual entry.
Emit(World &world, flags_t annex)
void emit_phi_args(Lam *callee, const App *app, Lam *pred)
Wires all non-mem.M arguments of app into callee's phis, coming from predecessor pred.
Emitter(World &world, std::string name, std::ostream &ostream)
virtual std::optional< std::string > isa_targetspecific_intrinsic(BB &, const Def *)
std::string convert_ret_pi(const Pi *)
void emit_phi(Lam *callee, const Def *phi, std::string arg, Lam *pred)
Registers arg as an incoming phi value for phi in callee, coming from predecessor pred.
void declare(std::format_string< Args... > s, Args &&... args)
std::string id(const Def *, bool force_bb=false) const
void declare_rt(std::format_string< Args... > sig, Args &&... args)
Declares a runtime wrapper sig (implemented in a C runtime, see add_mim_runtime) and records that the...
virtual std::string emit_slot(BB &bb, const App *app, const Def *pointee, const Def *)
Emits the storage backing a mem.slot of type pointee and yields the pointer value.
std::ostringstream vars_decls_
std::ostringstream func_impls_
@ embed
Splice the wrapper IR into the emitted module so it is self-contained.
@ ext
Only declare the wrappers; the runtime is linked in externally.
virtual std::string convert(const Def *type, bool simd=true)
std::ostringstream type_decls_
LamMap< const Def * > simd_phi_
@ nuw
No Unsigned Wrap around.
@ nsw
No Signed Wrap around.
static std::optional< std::pair< nat_t, const Def * > > is_simd(const Def *type)
void mim_ll_finalize(Emitter &)
void mim_ll_emit_epilogue(Emitter &, Lam *)
void mim_ll_convert(Emitter &, const Def *, bool simd, std::string &res)
The heavy, target-independent emitter methods are compiled once into libmim_ll (see ll....
static const Def * find_common_simd_src(const App *app)
static std::optional< std::pair< nat_t, const Def * > > is_simd_aggregate(Defs types)
void mim_ll_emit_bb(Emitter &, BB &, const Def *, std::string &res)
Mode
Allowed optimizations for a specific operation.
@ afn
Approximate functions.
@ reassoc
Allow reassociation transformations for floating-point operations.
@ contract
Allow floating-point contraction (e.g.
std::optional< nat_t > isa_f(const Def *def)
const Def * pointee(const Def *ptr)
Vector< const Def * > DefVec
absl::flat_hash_map< flags_t, std::function< std::unique_ptr< Phase >(World &)> > Flags2Phases
Maps an axiom of a Phase to a function that creates one.
mim::Plugin mim_get_plugin()
Vector(I, I, A=A()) -> Vector< typename std::iterator_traits< I >::value_type, Default_Inlined_Size< typename std::iterator_traits< I >::value_type >, A >
static consteval flags_t base()
Basic info and registration function pointer to be returned from a specific plugin.