11#include <absl/container/btree_set.h>
43#define MIM_LL_BE "ll backend: "
49inline const char* math_suffix(
const Def* type) {
56 fe::throwf(
MIM_LL_BE "unsupported floating-point type `{}`", type);
59inline const char* llvm_suffix(
const Def* type) {
62 case 16:
return ".f16";
63 case 32:
return ".f32";
64 case 64:
return ".f64";
67 fe::throwf(
MIM_LL_BE "unsupported floating-point type `{}`", type);
72inline const Def* isa_mem_sigma_2(
const Def* type) {
73 if (
auto sigma = type->isa<Sigma>())
82 BB(
BB&& other)
noexcept =
default;
85 std::deque<std::ostringstream>&
head() {
return parts[0]; }
86 std::deque<std::ostringstream>&
body() {
return parts[1]; }
87 std::deque<std::ostringstream>&
tail() {
return parts[2]; }
89 template<
class... Args>
90 inline std::string
assign(std::string_view name, std::format_string<Args...> s, Args&&... args) {
91 auto& os =
body().emplace_back();
92 std::print(os,
"{} = ", name);
93 std::print(os, s, std::forward<Args>(args)...);
94 return std::string(name);
97 template<
class... Args>
98 inline void tail(std::format_string<Args...> s, Args&&... args) {
99 std::print(
tail().emplace_back(), s, std::forward<Args>(args)...);
104 swap(a.phis, b.phis);
105 swap(a.parts, b.parts);
109 std::array<std::deque<std::ostringstream>, 3>
parts;
140 bool is_valid(std::string_view s) {
return !s.empty(); }
141 void start()
override;
150 emit_bb_(*
this, bb, def, res);
156 template<
class... Args>
158 std::ostringstream decl;
160 std::print(decl, s, std::forward<Args>(
args)...);
161 decls_.emplace(decl.str());
186 template<
class... Args>
193 std::string
id(
const Def*,
bool force_bb =
false)
const;
194 virtual std::string
convert(
const Def* type,
bool simd =
true) {
196 convert_(*
this, type, simd, res);
203 lam2bb_[callee].phis[phi].emplace_back(std::move(arg),
id(pred,
true));
210 for (
size_t i = 0; i != n; ++i)
212 auto phi = callee->
var(n, i);
214 emit_phi(callee, phi, std::move(arg), pred);
224 std::print(bb.
body().emplace_back(),
"{} = alloca {}", v_ptr,
convert(pointee,
false));
247 std::string convert_impl(
const Def*,
bool simd);
248 void finalize_impl();
249 void emit_epilogue_impl(
Lam*);
250 std::string emit_bb_impl(
BB&,
const Def*);
255 MIM_NOINLINE std::pair<std::string, std::string> emit_gep_index(
BB&,
const std::string&
name,
const Def* index);
277inline static std::optional<std::pair<nat_t, const Def*>>
is_simd(
const Def* type) {
278 if (
auto arr = type->isa<
Arr>()) {
279 if (
auto l =
Lit::isa(arr->arity())) {
281 return std::pair{*l, arr->body()};
288 if (std::ranges::all_of(types, [&](
auto i) {
return i == types[0]; })) {
290 return std::pair{types.size(), types[0]};
297 const Def* common_src =
nullptr;
299 for (
auto arg : app->args()) {
301 auto extract = arg->isa<
Extract>();
302 if (!extract || !
is_simd(extract->tuple()->type()))
return nullptr;
305 if (
auto index =
Lit::isa(extract->index()); !index || *index != lane++)
return nullptr;
307 common_src = extract->tuple();
308 else if (common_src != extract->tuple())
313 if (!simd || simd->first != lane)
return nullptr;
321 if (
auto lam = def->
isa_mut<
Lam>(); lam && !force_bb) {
322 if (lam->type()->ret_pi()) {
323 if (lam->is_external() || !lam->is_set())
324 return std::string(
"@") + lam->sym().str();
325 return std::string(
"@") + lam->unique_name();
334 if (dom ==
world().sigma())
return "void";
349 "`-X ll:rt=embed` needs the runtime module `mim_rt.ll`, but it "
350 "was not found (build with clang / `MIM_BUILD_LL_RUNTIME=ON`, or use `-X ll:rt=extern`)");
355 for (
auto&& decl :
decls_)
363 std::println(
ostream(),
"!{} = !{{!\"llvm.loop.vectorize.enable\", i1 true}}",
LoopMdBase);
364 for (
const auto& [_, md] :
loop_md_)
370 for (
const auto& dir :
world().
driver().rt_paths()) {
371 auto path = dir / std::string(filename);
373 if (!std::filesystem::is_regular_file(path, ec) || ec)
continue;
374 if (
auto ifs = std::ifstream(path)) {
375 world().
log().d(
"ll backend: load runtime module `{}`", path.string());
376 rt_module_.assign(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());
387 auto doms = lam->doms();
388 for (
auto sep =
"";
auto dom : doms.view().rsubspan(1)) {
400 std::print(
func_impls_,
"define {} {} {}(", internal, ret_t,
id(
root()));
403 for (
auto sep =
"";
auto var : vars.view().rsubspan(1)) {
405 if (
auto sigma = var->type()->isa<
Sigma>(); sigma && sigma->
num_ops() == 0)
continue;
406 if (
auto arr = var->type()->isa<
Arr>())
A (possibly paramterized) Array.
static auto isa(const Def *def)
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
const Def * op(size_t i) const noexcept
const Def * var(nat_t a, nat_t i) noexcept
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
nat_t num_tvars() noexcept
bool is_external() const noexcept
std::string unique_name() const
name + "_" + Def::gid
constexpr size_t num_ops() const noexcept
void load(std::string_view name)
bool is_loaded(std::string_view name) const
DefMap< std::string > locals_
std::ostream & ostream() const
std::string emit_unsafe(const Def *def)
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
static std::optional< T > isa(const Def *def)
const fe::Vector< std::string > & args()
Command-line arguments passed to this Phase's plugin via -X <plugin>:<arg>.
std::string_view name() const
virtual void start()=0
Actual entry.
A dependent function type.
const Pi * ret_pi() const
Yields the last Pi::dom, if Pi::isa_basicblock.
The World represents the whole program and manages creation of MimIR nodes (Defs).
const fe::Log & log() const
Log via log().e("...", args) etc.; owned by the Driver.
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.
void rt_module(std::string ll)
Provides the textual LLVM IR of the runtime module to splice in Rt::embed mode.
virtual std::string prepare()
Emitter(World &world, std::string name, std::ostream &ostream)
virtual std::optional< std::string > isa_targetspecific_intrinsic(BB &, const Def *)
void emit_imported(Lam *)
static constexpr u64 LoopMdBase
Loop-metadata node id per ll.vec-annotated loop header (see emit_epilogue_impl); numbered from LoopMd...
absl::btree_set< std::string > decls_
bool is_valid(std::string_view s)
friend void mim_ll_convert(Emitter &, const Def *, bool, std::string &)
The heavy, target-independent emitter methods are compiled once into libmim_ll (see ll....
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)
bool load_rt_module(std::string_view filename)
Locates the runtime module rt/<filename> (produced by add_mim_runtime) in the driver's search paths,...
std::ostringstream func_decls_
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_
Rt
How the C runtime wrappers (compiled to a <name>.ll via add_mim_runtime) reach the output.
@ 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.
mim::Emitter< std::string, std::string, BB, Emitter > Super
void start() override
Actual entry.
virtual void emit_epilogue(Lam *lam)
friend void mim_ll_finalize(Emitter &)
virtual std::string convert(const Def *type, bool simd=true)
std::string emit_bb(BB &bb, const Def *def)
std::ostringstream type_decls_
LamMap< const Def * > simd_phi_
friend void mim_ll_emit_bb(Emitter &, BB &, const Def *, std::string &)
friend void mim_ll_emit_epilogue(Emitter &, Lam *)
#define MIM_LL_BE
Prefix for this backend's fe::throwf messages; concatenate it with the format literal.
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)
std::optional< nat_t > isa_f(const Def *def)
const Def * strip_mem_ty(const Def *def)
Removes recusively all occurences of mem from a type (sigma).
fe::View< const Def * > Defs
GIDMap< const Def *, To > DefMap
GIDMap< Lam *, To > LamMap
std::deque< std::ostringstream > & tail()
std::deque< std::ostringstream > & body()
DefMap< std::deque< std::pair< std::string, std::string > > > phis
BB & operator=(BB other) noexcept
BB(BB &&other) noexcept=default
std::array< std::deque< std::ostringstream >, 3 > parts
friend void swap(BB &a, BB &b) noexcept
std::deque< std::ostringstream > & head()
void tail(std::format_string< Args... > s, Args &&... args)
std::string assign(std::string_view name, std::format_string< Args... > s, Args &&... args)