11#include <absl/container/btree_set.h>
43inline const char* math_suffix(
const Def* type) {
50 fe::throwf(
"unsupported floating point type '{}'", type);
53inline const char* llvm_suffix(
const Def* type) {
56 case 16:
return ".f16";
57 case 32:
return ".f32";
58 case 64:
return ".f64";
61 fe::throwf(
"unsupported floating point type '{}'", type);
66inline const Def* isa_mem_sigma_2(
const Def* type) {
67 if (
auto sigma = type->isa<
Sigma>())
76 BB(
BB&& other)
noexcept =
default;
79 std::deque<std::ostringstream>&
head() {
return parts[0]; }
80 std::deque<std::ostringstream>&
body() {
return parts[1]; }
81 std::deque<std::ostringstream>&
tail() {
return parts[2]; }
83 template<
class... Args>
84 inline std::string
assign(std::string_view name, std::format_string<Args...> s, Args&&... args) {
85 auto& os =
body().emplace_back();
86 std::print(os,
"{} = ", name);
87 std::print(os, s, std::forward<Args>(args)...);
88 return std::string(name);
91 template<
class... Args>
92 inline void tail(std::format_string<Args...> s, Args&&... args) {
93 std::print(
tail().emplace_back(), s, std::forward<Args>(args)...);
96 friend inline void swap(
BB& a,
BB& b)
noexcept {
99 swap(a.parts, b.parts);
103 std::array<std::deque<std::ostringstream>, 3>
parts;
134 bool is_valid(std::string_view s) {
return !s.empty(); }
135 void start()
override;
144 emit_bb_(*
this, bb, def, res);
150 template<
class... Args>
152 std::ostringstream decl;
154 std::print(decl, s, std::forward<Args>(
args)...);
155 decls_.emplace(decl.str());
180 template<
class... Args>
187 std::string
id(
const Def*,
bool force_bb =
false)
const;
188 virtual std::string
convert(
const Def* type,
bool simd =
true) {
190 convert_(*
this, type, simd, res);
197 lam2bb_[callee].phis[phi].emplace_back(std::move(arg),
id(pred,
true));
204 for (
size_t i = 0; i != n; ++i)
206 auto phi = callee->
var(n, i);
208 emit_phi(callee, phi, std::move(arg), pred);
218 std::print(bb.
body().emplace_back(),
"{} = alloca {}", v_ptr,
convert(pointee,
false));
235 std::string convert_impl(
const Def*,
bool simd);
236 void finalize_impl();
237 void emit_epilogue_impl(
Lam*);
238 std::string emit_bb_impl(
BB&,
const Def*);
255inline static std::optional<std::pair<nat_t, const Def*>>
is_simd(
const Def* type) {
256 if (
auto arr = type->isa<
Arr>()) {
257 if (
auto l =
Lit::isa(arr->arity())) {
259 return std::pair{*l, arr->body()};
266 if (std::ranges::all_of(types, [&](
auto i) {
return i == types[0]; })) {
268 return std::pair{types.size(), types[0]};
275 const Def* common_src =
nullptr;
277 for (
auto arg : app->args()) {
279 auto extract = arg->isa<
Extract>();
280 if (!extract || !
is_simd(extract->tuple()->type()))
return nullptr;
283 if (
auto index =
Lit::isa(extract->index()); !index || *index != lane++)
return nullptr;
285 common_src = extract->tuple();
286 else if (common_src != extract->tuple())
291 if (!simd || simd->first != lane)
return nullptr;
299 if (
auto lam = def->
isa_mut<
Lam>(); lam && !force_bb) {
300 if (lam->type()->ret_pi()) {
301 if (lam->is_external() || !lam->is_set())
302 return std::string(
"@") + lam->sym().str();
303 return std::string(
"@") + lam->unique_name();
312 if (dom ==
world().sigma())
return "void";
326 fe::throwf(
"ll backend: `-X ll:rt=embed` needs the runtime module `mim_rt.ll`, but it "
327 "was not found (build with clang / `MIM_BUILD_LL_RUNTIME=ON`, or use `-X ll:rt=extern`)");
332 for (
auto&& decl :
decls_)
340 for (
const auto& dir :
world().
driver().search_paths()) {
341 auto path = dir /
"rt" / std::string(filename);
343 if (!std::filesystem::is_regular_file(path, ec) || ec)
continue;
344 if (
auto ifs = std::ifstream(path)) {
345 world().DLOG(
"ll backend: loaded runtime module `{}`", path.string());
346 rt_module_.assign(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());
357 auto doms = lam->doms();
358 for (
auto sep =
"";
auto dom : doms.view().rsubspan(1)) {
370 std::print(
func_impls_,
"define {} {} {}(", internal, ret_t,
id(
root()));
373 for (
auto sep =
"";
auto var : vars.view().rsubspan(1)) {
375 if (
auto sigma = var->type()->isa<
Sigma>(); sigma && sigma->
num_ops() == 0)
continue;
376 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
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)
std::string_view name() const
virtual void start()=0
Actual entry.
const Vector< std::string > & args()
Command-line arguments passed to this Phase's plugin via -X <plugin>:<arg>.
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).
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 *)
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 *)
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).
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)