378 auto app = lam->
body()->as<
App>();
380 if (app->callee() ==
root()->ret_var()) {
381 std::vector<std::string> values;
382 std::vector<const Def*> types;
383 for (
auto arg : app->args()) {
385 values.emplace_back(val);
386 types.emplace_back(arg->type());
390 switch (values.size()) {
391 case 0:
return bb.tail(
"ret void");
392 case 1:
return bb.tail(
"ret {} {}", convert(types[0]), values[0]);
400 auto v_src =
emit(common_src);
401 auto t = convert(common_src->type());
402 return bb.tail(
"ret {} {}", t, v_src);
404 auto [size, elem] = *se;
405 auto val_t = convert(elem);
407 type = std::format(
"<{} x {}>", size, val_t);
408 for (
auto val : values) {
413 prev += std::format(
"{} {}", val_t, val);
418 type = convert(
world().sigma(types));
419 for (
size_t i = 0, n = values.size(); i != n; ++i) {
420 auto v_elem = values[i];
421 auto t_elem = convert(types[i]);
422 auto namei =
"%ret_val." + std::to_string(i);
423 bb.tail(
"{} = insertvalue {} {}, {} {}, {}", namei, type, prev, t_elem, v_elem, i);
427 bb.tail(
"ret {} {}", type, prev);
431 }
else if (
auto dispatch =
Dispatch(app)) {
432 for (
auto callee : dispatch.tuple()->projs([](
const Def* def) { return def->isa_mut<Lam>(); })) {
433 size_t n = callee->num_tvars();
434 if (n == 1 &&
is_simd(callee->var(0)->type())) {
435 auto phi = callee->var(0);
436 auto arg =
emit(app->arg(n, 0));
437 lam2bb_[callee].phis[phi].emplace_back(arg,
id(lam,
true));
440 for (
size_t i = 0; i != n; ++i) {
441 if (
auto arg =
emit_unsafe(app->arg(n, i)); !arg.empty()) {
442 auto phi = callee->var(n, i);
444 lam2bb_[callee].phis[phi].emplace_back(arg,
id(lam,
true));
451 auto v_index =
emit(dispatch.index());
452 size_t n = dispatch.num_targets();
453 auto bbs = absl::FixedArray<std::string>(n);
454 for (
size_t i = 0; i != n; ++i)
455 bbs[i] =
emit(dispatch.target(i));
457 if (
auto branch =
Branch(app))
return bb.tail(
"br i1 {}, label {}, label {}", v_index, bbs[1], bbs[0]);
459 auto t_index = convert(dispatch.index()->type());
460 bb.tail(
"switch {} {}, label {} [ ", t_index, v_index, bbs[0]);
461 for (
size_t i = 1; i != n; ++i)
462 std::print(bb.tail().back(),
"{} {}, label {} ", t_index, std::to_string(i), bbs[i]);
463 std::print(bb.tail().back(),
"]");
464 }
else if (app->callee()->isa<
Bot>()) {
465 return bb.tail(
"ret ; bottom: unreachable");
470 auto v_src =
emit(common_src);
471 auto callee_var = callee->var();
472 if (simd_phi_.find(callee) == simd_phi_.end()) simd_phi_[callee] = callee_var;
473 auto key = simd_phi_[callee];
474 lam2bb_[callee].phis[key].emplace_back(v_src,
id(lam,
true));
476 for (
auto var : callee->vars())
480 size_t n = callee->num_tvars();
481 for (
size_t i = 0; i != n; ++i) {
482 if (
auto arg =
emit_unsafe(app->arg(n, i)); !arg.empty()) {
483 auto phi = callee->var(n, i);
485 lam2bb_[callee].phis[phi].emplace_back(arg,
id(lam,
true));
490 return bb.tail(
"br label {}",
id(callee));
493 declare(
"void @longjmp(i8*, i32) noreturn");
495 auto [
mem, jbuf, tag] = app->args<3>();
497 auto v_jb =
emit(jbuf);
498 auto v_tag =
emit(tag);
499 bb.tail(
"call void @longjmp(i8* {}, i32 {})", v_jb, v_tag);
500 return bb.tail(
"unreachable");
502 auto v_callee =
emit(app->callee());
504 std::vector<std::string> args;
505 auto app_args = app->args();
506 for (
auto arg : app_args.view().rsubspan(1))
507 if (
auto v_arg =
emit_unsafe(arg); !v_arg.empty()) args.emplace_back(convert(arg->type()) +
" " + v_arg);
509 if (app->args().back()->isa<
Bot>()) {
511 assert(convert_ret_pi(app->callee_type()->ret_pi()) ==
"void");
512 bb.tail(
"call void {}({})", v_callee, fe::Join(args));
513 return bb.tail(
"unreachable");
516 auto ret_lam = app->args().back()->as_mut<
Lam>();
517 size_t num_vars = ret_lam->
num_vars();
521 for (
auto var : ret_lam->vars()) {
524 types[n] = var->type();
529 bb.tail(
"call void {}({})", v_callee, fe::Join(args));
531 auto name =
"%" + app->unique_name() +
"ret";
532 auto t_ret = convert_ret_pi(ret_lam->type());
533 bb.tail(
"{} = call {} {}({})",
name, t_ret, v_callee, fe::Join(args));
534 auto phi = ret_lam->
var();
535 lam2bb_[ret_lam].phis[phi].emplace_back(
name,
id(lam,
true));
539 return bb.tail(
"br label {}",
id(ret_lam));
544 if (
auto lam = def->isa<
Lam>())
return id(lam);
549 auto emit_tuple = [&](
const Def*
tuple) {
550 if (isa_mem_sigma_2(
tuple->type())) {
555 if (
tuple->is_closed()) {
556 bool is_array =
tuple->type()->isa<
Arr>();
557 auto simd_array = convert(
tuple->type()).front() ==
'<';
559 s += simd_array ?
"<" : is_array ?
"[" :
"{";
561 for (
size_t i = 0, n =
tuple->num_projs(); i != n; ++i) {
562 auto e =
tuple->proj(n, i);
563 if (
auto v_elem =
emit_unsafe(e); !v_elem.empty()) {
564 auto t_elem = convert(e->type());
565 s += sep + t_elem +
" " + v_elem;
570 return s += simd_array ?
">" : is_array ?
"]" :
"}";
573 std::string prev =
"undef";
574 auto t = convert(
tuple->type());
575 for (
size_t src = 0, dst = 0, n =
tuple->num_projs(); src != n; ++src) {
576 auto e =
tuple->proj(n, src);
578 auto elem_t = convert(e->type());
580 auto namei =
name +
"." + std::to_string(dst);
581 if (t.front() ==
'<')
582 prev = bb.
assign(namei,
"insertelement {} {}, {} {}, {} {}", t, prev, elem_t, elem, elem_t, dst);
584 prev = bb.
assign(namei,
"insertvalue {} {}, {} {}, {}", t, prev, elem_t, elem, dst);
591 if (def->isa<
Var>()) {
594 if (std::ranges::any_of(ts, [](
auto t) {
return Axm::isa<mem::M>(t); }))
return {};
595 return emit_tuple(def);
598 auto emit_gep_index = [&](
const Def* index) {
599 auto v_i =
emit(index);
600 auto t_i = convert(index->type());
602 if (
auto size =
Idx::isa(index->type())) {
605 "zext {} {} to i{} ; add one more bit for gep index as it is treated as signed value",
607 t_i =
"i" + std::to_string(*w + 1);
611 return std::pair(v_i, t_i);
614 if (
auto lit = def->isa<
Lit>()) {
615 if (lit->type()->isa<
Nat>() ||
Idx::isa(lit->type())) {
616 return std::to_string(lit->get());
623 s <<
"0xH" << std::setfill(
'0') << std::setw(4) << std::right << std::hex << lit->get<
u16>();
626 hex = std::bit_cast<u64>(
f64(lit->get<
f32>()));
629 case 64: hex = lit->get<
u64>();
break;
630 default: fe::unreachable();
633 s <<
"0x" << std::setfill(
'0') << std::setw(16) << std::right << std::hex << hex;
637 }
else if (def->isa<
Bot>()) {
639 }
else if (
auto top = def->isa<
Top>()) {
643 return emit_tuple(
tuple);
644 }
else if (
auto pack = def->isa<
Pack>()) {
645 if (
auto lit =
Lit::isa(pack->body()); lit && *lit == 0)
return "zeroinitializer";
646 return emit_tuple(pack);
647 }
else if (
auto sel =
Select(def)) {
648 auto t = convert(sel.extract()->type());
649 auto [elem_a, elem_b] = sel.pair()->projs<2>([&](
auto e) {
return emit_unsafe(e); });
650 auto cond_t = convert(sel.cond()->type());
651 auto cond =
emit(sel.cond());
652 return bb.
assign(
name,
"select {} {}, {} {}, {} {}", cond_t, cond, t, elem_b, t, elem_a);
653 }
else if (
auto extract = def->isa<
Extract>()) {
654 auto tuple = extract->tuple();
655 auto index = extract->index();
663 auto t_tup = convert(
tuple->type());
665 if (isa_mem_sigma_2(
tuple->type()))
return v_tup;
669 return bb.
assign(
name,
"extractvalue {} {}, {}", t_tup, v_tup, v_i);
672 auto t_elem = convert(extract->type());
673 auto [v_i, t_i] = emit_gep_index(index);
676 "{}.alloca = alloca {} ; copy to alloca to emulate extract with store + gep + load",
name, t_tup);
677 std::print(bb.
body().emplace_back(),
"store {} {}, {}* {}.alloca", t_tup, v_tup, t_tup,
name);
678 std::print(bb.
body().emplace_back(),
"{}.gep = getelementptr inbounds {}, {}* {}.alloca, i64 0, {} {}",
name,
679 t_tup, t_tup,
name, t_i, v_i);
680 return bb.
assign(
name,
"load {}, {}* {}.gep", t_elem, t_elem,
name);
681 }
else if (
auto insert = def->isa<
Insert>()) {
683 auto t_tup = convert(insert->tuple()->type());
684 auto t_val = convert(insert->value()->type());
685 auto v_tup =
emit(insert->tuple());
686 auto v_val =
emit(insert->value());
687 if (
auto idx =
Lit::isa(insert->index())) {
688 auto v_idx =
emit(insert->index());
689 if (
is_simd(insert->tuple()->type()))
691 return bb.
assign(
name,
"insertelement {} {}, {} {}, i32 {}", t_tup, v_tup, t_val, v_val, v_idx);
694 return bb.
assign(
name,
" insertvalue {} {}, {} {}, {}", t_tup, v_tup, t_val, v_val, v_idx);
696 if (
is_simd(insert->tuple()->type())) {
697 auto v_i =
emit(insert->index());
698 auto t_i = convert(insert->index()->type());
701 v_i = bb.
assign(
name +
".idx",
"{} {} {} to i32", w_src < 32 ?
"zext" :
"trunc", t_i, v_i);
703 return bb.
assign(
name,
"insertelement {} {}, {} {}, i32 {}", t_tup, v_tup, t_val, v_val, v_i);
705 auto t_elem = convert(insert->value()->type());
706 auto [v_i, t_i] = emit_gep_index(insert->index());
708 "{}.alloca = alloca {} ; copy to alloca to emulate insert with store + gep + load",
name, t_tup);
709 std::print(bb.
body().emplace_back(),
"store {} {}, {}* {}.alloca", t_tup, v_tup, t_tup,
name);
710 std::print(bb.
body().emplace_back(),
"{}.gep = getelementptr inbounds {}, {}* {}.alloca, i64 0, {} {}",
711 name, t_tup, t_tup,
name, t_i, v_i);
712 std::print(bb.
body().emplace_back(),
"store {} {}, {}* {}.gep", t_val, v_val, t_val,
name);
713 return bb.
assign(
name,
"load {}, {}* {}.alloca", t_tup, t_tup,
name);
715 }
else if (
auto global = def->isa<
Global>()) {
716 auto v_init =
emit(global->init());
718 std::print(vars_decls_,
"{} = global {} {}\n",
name, convert(pointee), v_init);
721 auto [a, b] = nat->args<2>([
this](
auto def) {
return emit(def); });
729 return bb.
assign(
name,
"{} nsw nuw i64 {}, {}", op, a, b);
731 auto [a, b] = ncmp->args<2>([
this](
auto def) {
return emit(def); });
743 default: fe::unreachable();
746 return bb.
assign(
name,
"{} i64 {}, {}", op, a, b);
748 auto x =
emit(idx->arg());
750 auto t = convert(idx->type());
751 if (s < 64)
return bb.
assign(
name,
"trunc i64 {} to {}", x, t);
755 auto x =
emit(bit1->arg());
756 auto t = convert(bit1->type());
757 return bb.
assign(
name,
"xor {} -1, {}", t, x);
759 auto [a, b] = bit2->args<2>([
this](
auto def) {
return emit(def); });
760 auto t = convert(bit2->type());
762 auto neg = [&](std::string_view x) {
return bb.
assign(
name +
".neg",
"xor {} -1, {}", t, x); };
775 default: fe::unreachable();
778 auto [a, b] = shr->args<2>([
this](
auto def) {
return emit(def); });
779 auto t = convert(shr->type());
786 return bb.
assign(
name,
"{} {} {}, {}", op, t, a, b);
788 auto [mode, ab] = wrap->uncurry_args<2>();
789 auto [a, b] = ab->projs<2>([
this](
auto def) {
return emit(def); });
790 auto t = convert(wrap->type());
803 return bb.
assign(
name,
"{} {} {}, {}", op, t, a, b);
805 auto [m, xy] = div->args<2>();
806 auto [x, y] = xy->projs<2>();
807 auto t = convert(x->type());
819 return bb.
assign(
name,
"{} {} {}, {}", op, t, a, b);
821 auto [a, b] = icmp->args<2>([
this](
auto def) {
return emit(def); });
822 auto t = convert(icmp->arg(0)->type());
838 default: fe::unreachable();
841 return bb.
assign(
name,
"{} {} {}, {}", op, t, a, b);
843 auto [x, y] = extr->args<2>();
844 auto t = convert(x->type());
847 std::string f =
"llvm.";
855 declare(
"{} @{}({}, {})", t, f, t, t);
856 return bb.
assign(
name,
"tail call {} @{}({} {}, {} {})", t, f, t, a, t, b);
858 auto [m, x] = abs->args<2>();
859 auto t = convert(x->type());
861 std::string f =
"llvm.abs." + t;
862 declare(
"{} @{}({}, {})", t, f, t,
"i1");
863 return bb.
assign(
name,
"tail call {} @{}({} {}, {} {})", t, f, t, a,
"i1",
"1");
865 auto v_src =
emit(conv->arg());
866 auto t_src = convert(conv->arg()->type());
867 auto t_dst = convert(conv->type());
872 if (w_src == w_dst)
return v_src;
875 case core::conv::s: op = w_src < w_dst ?
"sext" :
"trunc";
break;
876 case core::conv::u: op = w_src < w_dst ?
"zext" :
"trunc";
break;
879 return bb.
assign(
name,
"{} {} {} to {}", op, t_src, v_src, t_dst);
883 auto v_src =
emit(bitcast->arg());
884 auto t_src = convert(bitcast->arg()->type());
885 auto t_dst = convert(bitcast->type());
887 if (
auto lit =
Lit::isa(bitcast->arg()); lit && *lit == 0)
return "zeroinitializer";
889 if (src_type_ptr && dst_type_ptr)
return bb.
assign(
name,
"bitcast {} {} to {}", t_src, v_src, t_dst);
890 if (src_type_ptr)
return bb.
assign(
name,
"ptrtoint {} {} to {}", t_src, v_src, t_dst);
891 if (dst_type_ptr)
return bb.
assign(
name,
"inttoptr {} {} to {}", t_src, v_src, t_dst);
894 auto size2width = [&](
const Def* type) {
895 if (type->isa<
Nat>())
return 64_n;
900 auto src_size = size2width(bitcast->arg()->type());
901 auto dst_size = size2width(bitcast->type());
904 if (src_size && dst_size) {
905 if (src_size == dst_size)
return v_src;
906 op = (src_size < dst_size) ?
"zext" :
"trunc";
908 return bb.
assign(
name,
"{} {} {} to {}", op, t_src, v_src, t_dst);
910 auto [ptr, i] = lea->args<2>();
912 auto v_ptr =
emit(ptr);
913 auto t_pointee = convert(pointee);
914 auto t_ptr = convert(ptr->type());
915 if (pointee->isa<
Sigma>())
916 return bb.
assign(
name,
"getelementptr inbounds {}, {} {}, i64 0, i32 {}", t_pointee, t_ptr, v_ptr,
919 assert(pointee->isa<
Arr>());
920 auto [v_i, t_i] = emit_gep_index(i);
922 return bb.
assign(
name,
"getelementptr inbounds {}, {} {}, i64 0, {} {}", t_pointee, t_ptr, v_ptr, t_i, v_i);
927 auto size =
emit(malloc->arg(1));
929 bb.
assign(
name +
"i8",
"call i8* @malloc(i64 {})", size);
930 return bb.
assign(
name,
"bitcast i8* {} to {}",
name +
"i8", ptr_t);
934 auto ptr =
emit(free->arg(1));
937 bb.
assign(
name +
"i8",
"bitcast {} {} to i8*", ptr_t, ptr);
938 bb.
tail(
"call void @free(i8* {})",
name +
"i8");
941 auto [Ta, msi] = mslot->uncurry_args<2>();
942 auto [pointee, addr_space] = Ta->projs<2>();
946 std::print(bb.
body().emplace_back(),
"{} = alloca {}",
name, convert(pointee,
false));
950 auto v_ptr =
emit(load->arg(1));
951 auto t_ptr = convert(load->arg(1)->type());
952 auto t_pointee = convert(
Axm::as<mem::Ptr>(load->arg(1)->type())->arg(0),
false);
953 return bb.
assign(
name,
"load {}, {} {}", t_pointee, t_ptr, v_ptr);
956 auto v_ptr =
emit(store->arg(1));
957 auto v_val =
emit(store->arg(2));
958 auto t_ptr = convert(store->arg(1)->type());
959 auto t_val = convert(store->arg(2)->type(),
false);
960 std::print(bb.
body().emplace_back(),
"store {} {}, {} {}", t_val, v_val, t_ptr, v_ptr);
966 auto size =
name +
".size";
967 bb.
assign(size,
"call i64 @jmpbuf_size()");
968 return bb.
assign(
name,
"alloca i8, i64 {}", size);
970 declare(
"i32 @_setjmp(i8*) returns_twice");
972 auto [
mem, jmpbuf] = setjmp->arg()->projs<2>();
974 auto v_jb =
emit(jmpbuf);
975 return bb.
assign(
name,
"call i32 @_setjmp(i8* {})", v_jb);
977 auto [mode, ab] = arith->uncurry_args<2>();
978 auto [a, b] = ab->projs<2>([
this](
auto def) {
return emit(def); });
979 auto t = convert(arith->type());
982 switch (arith.id()) {
1004 return bb.
assign(
name,
"{} {} {}, {}", op, t, a, b);
1006 auto a =
emit(tri->arg());
1007 auto t = convert(tri->type());
1012 f = std::string(
"llvm.sin") + llvm_suffix(tri->type());
1014 f = std::string(
"llvm.cos") + llvm_suffix(tri->type());
1023 default: fe::unreachable();
1027 f += math_suffix(tri->type());
1030 declare(
"{} @{}({})", t, f, t);
1031 return bb.
assign(
name,
"tail call {} @{}({} {})", t, f, t, a);
1033 auto [a, b] = extrema->args<2>([
this](
auto def) {
return emit(def); });
1034 auto t = convert(extrema->type());
1035 std::string f =
"llvm.";
1036 switch (extrema.id()) {
1042 f += llvm_suffix(extrema->type());
1044 declare(
"{} @{}({}, {})", t, f, t, t);
1045 return bb.
assign(
name,
"tail call {} @{}({} {}, {} {})", t, f, t, a, t, b);
1047 auto [a, b] = pow->args<2>([
this](
auto def) {
return emit(def); });
1048 auto t = convert(pow->type());
1049 std::string f =
"llvm.pow";
1050 f += llvm_suffix(pow->type());
1051 declare(
"{} @{}({}, {})", t, f, t, t);
1052 return bb.
assign(
name,
"tail call {} @{}({} {}, {} {})", t, f, t, a, t, b);
1054 auto a =
emit(rt->arg());
1055 auto t = convert(rt->type());
1058 f = std::string(
"llvm.sqrt") + llvm_suffix(rt->type());
1060 f = std::string(
"cbrt") += math_suffix(rt->type());
1061 declare(
"{} @{}({})", t, f, t);
1062 return bb.
assign(
name,
"tail call {} @{}({} {})", t, f, t, a);
1064 auto a =
emit(exp->arg());
1065 auto t = convert(exp->type());
1066 std::string f =
"llvm.";
1069 f += llvm_suffix(exp->type());
1071 declare(
"{} @{}({})", t, f, t);
1072 return bb.
assign(
name,
"tail call {} @{}({} {})", t, f, t, a);
1074 auto a =
emit(er->arg());
1075 auto t = convert(er->type());
1076 auto f = er.id() ==
math::er::f ? std::string(
"erf") : std::string(
"erfc");
1077 f += math_suffix(er->type());
1078 declare(
"{} @{}({})", t, f, t);
1079 return bb.
assign(
name,
"tail call {} @{}({} {})", t, f, t, a);
1081 auto a =
emit(gamma->arg());
1082 auto t = convert(gamma->type());
1083 std::string f = gamma.id() ==
math::gamma::t ?
"tgamma" :
"lgamma";
1084 f += math_suffix(gamma->type());
1085 declare(
"{} @{}({})", t, f, t);
1086 return bb.
assign(
name,
"tail call {} @{}({} {})", t, f, t, a);
1088 auto [a, b] = cmp->args<2>([
this](
auto def) {
return emit(def); });
1089 auto t = convert(cmp->arg(0)->type());
1094 case math::cmp:: e: op +=
"oeq";
break;
1095 case math::cmp:: l: op +=
"olt";
break;
1097 case math::cmp:: g: op +=
"ogt";
break;
1100 case math::cmp:: o: op +=
"ord";
break;
1101 case math::cmp:: u: op +=
"uno";
break;
1109 default: fe::unreachable();
1112 return bb.
assign(
name,
"{} {} {}, {}", op, t, a, b);
1116 auto a =
emit(is_finite->arg());
1117 auto at = convert(is_finite->arg()->type());
1118 auto t = convert(is_finite->type());
1120 auto s = llvm_suffix(is_finite->arg()->type());
1121 auto f =
"llvm.is.fpclass";
1122 declare(
"{} @{}{}({}, i32)", t, f, s, at);
1123 return bb.
assign(
name,
"tail call {} @{}{}({} {}, i32 504)", t, f, s, at, a);
1125 auto v_src =
emit(conv->arg());
1126 auto t_src = convert(conv->arg()->type());
1127 auto t_dst = convert(conv->type());
1132 switch (conv.id()) {
1133 case math::conv::f2f: op = s_src < s_dst ?
"fpext" :
"fptrunc";
break;
1140 return bb.
assign(
name,
"{} {} {} to {}", op, t_src, v_src, t_dst);
1142 auto a =
emit(abs->arg());
1143 auto t = convert(abs->type());
1144 std::string f =
"llvm.fabs";
1145 f += llvm_suffix(abs->type());
1146 declare(
"{} @{}({})", t, f, t);
1147 return bb.
assign(
name,
"tail call {} @{}({} {})", t, f, t, a);
1149 auto a =
emit(round->arg());
1150 auto t = convert(round->type());
1151 std::string f =
"llvm.";
1152 switch (round.id()) {
1158 f += llvm_suffix(round->type());
1159 declare(
"{} @{}({})", t, f, t);
1160 return bb.
assign(
name,
"tail call {} @{}({} {})", t, f, t, a);
1162 auto ni_n = zip->decurry()->decurry()->decurry()->arg();
1163 auto nat_ni = *
Lit::isa(ni_n->proj(2, 0));
1164 auto f = zip->decurry()->arg();
1165 auto inputs = zip->arg();
1166 auto t_in = convert(inputs->proj(nat_ni, 0)->type());
1167 auto t_out = convert(def->
type());
1173 switch (nat_op.id()) {
1180 switch (arith_op.id()) {
1201 switch (ncmp_op.id()) {
1208 default: fe::unreachable();
1212 switch (icmp_op.id()) {
1223 default: fe::unreachable();
1227 switch (mcmp_op.id()) {
1242 default: fe::unreachable();
1245 error(
"unhandled vec.zip operation: {}", f);
1248 auto v1 =
emit(inputs->proj(nat_ni, 0));
1249 auto v2 =
emit(inputs->proj(nat_ni, 1));
1250 prev = bb.
assign(
name,
"{} {} {}, {}", op, t_in, v1, v2);
1255 error(
"unhandled def in LLVM backend: {} : {}", def, def->
type());