MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
ll.h
Go to the documentation of this file.
1#pragma once
2
3#include <deque>
4#include <format>
5#include <fstream>
6#include <iterator>
7#include <optional>
8#include <ostream>
9#include <string>
10
11#include <absl/container/btree_set.h>
12
13#include <mim/driver.h>
14
15#include <mim/be/emitter.h>
16
17#include <mim/plug/clos/clos.h>
18#include <mim/plug/math/math.h>
19#include <mim/plug/mem/mem.h>
20#include <mim/plug/vec/vec.h>
21
22#include "mim/plug/ll/autogen.h"
23
24// Lessons learned:
25// * **Always** follow all ops - even if you actually want to ignore one.
26// Otherwise, you might end up with an incorrect schedule.
27// This was the case for an Extract of type Mem.
28// While we want to ignore the value obtained from that, since there is no Mem value in LLVM,
29// we still want to **first** recursively emit code for its operands and **then** ignore the Extract itself.
30// * i1 has a different meaning in LLVM then in Mim:
31// * Mim: {0, 1} = i1
32// * LLVM: {0, -1} = i1
33// This is a problem when, e.g., using an index of type i1 as LLVM thinks like this:
34// getelementptr ..., i1 1 == getelementptr .., i1 -1
35namespace mim {
36
37class World;
38
39namespace plug::ll {
40
41/// Prefix for this backend's fe::throwf messages; concatenate it with the format literal.
42/// @note Not usable with the Def::expect family: those take the *inner* "what was expected" description.
43#define MIM_LL_BE "ll backend: "
44
45namespace math = mim::plug::math;
46namespace mem = mim::plug::mem;
47
48namespace detail {
49inline const char* math_suffix(const Def* type) {
50 if (auto w = math::isa_f(type)) {
51 switch (*w) {
52 case 32: return "f";
53 case 64: return "";
54 }
55 }
56 fe::throwf(MIM_LL_BE "unsupported floating-point type `{}`", type);
57}
58
59inline const char* llvm_suffix(const Def* type) {
60 if (auto w = math::isa_f(type)) {
61 switch (*w) {
62 case 16: return ".f16";
63 case 32: return ".f32";
64 case 64: return ".f64";
65 }
66 }
67 fe::throwf(MIM_LL_BE "unsupported floating-point type `{}`", type);
68}
69
70// [mem.M 0, T] => T
71// TODO there may be more instances where we have to deal with this trickery
72inline const Def* isa_mem_sigma_2(const Def* type) {
73 if (auto sigma = type->isa<Sigma>())
74 if (sigma->num_ops() == 2 && Axm::isa<mem::M>(sigma->op(0))) return sigma->op(1);
75 return {};
76}
77} // namespace detail
78
79struct BB {
80 BB() = default;
81 BB(const BB&) = delete;
82 BB(BB&& other) noexcept = default;
83 BB& operator=(BB other) noexcept { return swap(*this, other), *this; }
84
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]; }
88
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);
95 }
96
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)...);
100 }
101
102 friend inline void swap(BB& a, BB& b) noexcept {
103 using std::swap;
104 swap(a.phis, b.phis);
105 swap(a.parts, b.parts);
106 }
107
109 std::array<std::deque<std::ostringstream>, 3> parts;
110};
111
112class Emitter;
113
114/// The heavy, target-independent emitter methods are compiled once into `libmim_ll` (see ll.cpp).
115/// `libmim_ll_nvptx` reaches them through these `extern "C"` shims instead of recompiling the bodies;
116/// Emitter caches the pointers via `GET_FUN_PTR` in its constructor.
117extern "C" {
118MIM_EXPORT void mim_ll_convert(Emitter&, const Def*, bool simd, std::string& res);
121MIM_EXPORT void mim_ll_emit_bb(Emitter&, BB&, const Def*, std::string& res);
122}
123
124class Emitter : public mim::Emitter<std::string, std::string, BB, Emitter> {
125public:
127
128 Emitter(World& world, std::string name, std::ostream& ostream)
129 : Super(world, name, ostream) {
130 auto& driver = world.driver();
131 // Ensure libmim_ll is loaded so the shims below resolve (e.g. when a derived backend like
132 // ll_nvptx uses us). Loading merely registers ll.emit; it does not run it.
133 if (!driver.is_loaded("ll")) driver.load("ll");
134 convert_ = driver.GET_FUN_PTR("ll", mim_ll_convert);
135 finalize_ = driver.GET_FUN_PTR("ll", mim_ll_finalize);
136 emit_epilogue_ = driver.GET_FUN_PTR("ll", mim_ll_emit_epilogue);
137 emit_bb_ = driver.GET_FUN_PTR("ll", mim_ll_emit_bb);
138 }
139
140 bool is_valid(std::string_view s) { return !s.empty(); }
141 void start() override;
142 void emit_imported(Lam*);
143 virtual std::string prepare();
144
145 // Thin forwarders into `libmim_ll`; the real bodies (`*_impl`) live in ll.cpp.
146 virtual void emit_epilogue(Lam* lam) { emit_epilogue_(*this, lam); }
147 void finalize() { finalize_(*this); }
148 std::string emit_bb(BB& bb, const Def* def) {
149 std::string res;
150 emit_bb_(*this, bb, def, res);
151 return res;
152 }
153
154 virtual inline std::optional<std::string> isa_targetspecific_intrinsic(BB&, const Def*) { return std::nullopt; }
155
156 template<class... Args>
157 void declare(std::format_string<Args...> s, Args&&... args) {
158 std::ostringstream decl;
159 decl << "declare ";
160 std::print(decl, s, std::forward<Args>(args)...);
161 decls_.emplace(decl.str());
162 }
163
164 /// How the C runtime wrappers (compiled to a `<name>.ll` via `add_mim_runtime`) reach the output.
165 enum class Rt {
166 embed, ///< Splice the wrapper IR into the emitted module so it is self-contained.
167 ext, ///< Only `declare` the wrappers; the runtime is linked in externally.
168 };
169
170 void rt_mode(Rt rt) { rt_ = rt; }
171 /// Provides the textual LLVM IR of the runtime module to splice in `Rt::embed` mode.
172 void rt_module(std::string ll) { rt_module_ = std::move(ll); }
173
174 /// Locates the runtime module `rt/<filename>` (produced by `add_mim_runtime`) in the driver's
175 /// search paths, reads it, and stores it for `Rt::embed` splicing.
176 /// Backends share this instead of duplicating the lookup: `ll` loads `ll_rt.ll`, `ll_nvptx`
177 /// loads `ll_nvptx_rt.ll`, and so on — one merged module per plugin.
178 /// @returns whether the module was found.
179 bool load_rt_module(std::string_view filename);
180
181 /// Declares a runtime wrapper @p sig (implemented in a C runtime, see `add_mim_runtime`) and
182 /// records that the runtime is required by this module.
183 /// In `Rt::ext` mode the declaration is emitted like any other `declare`.
184 /// In `Rt::embed` mode the wrapper's *definition* is spliced into the output, so emitting a
185 /// `declare` as well would be a redefinition — hence it is suppressed here.
186 template<class... Args>
187 void declare_rt(std::format_string<Args...> sig, Args&&... args) {
188 rt_used_ = true;
189 if (rt_ == Rt::ext) declare(sig, std::forward<Args>(args)...);
190 }
191
192protected:
193 std::string id(const Def*, bool force_bb = false) const;
194 virtual std::string convert(const Def* type, bool simd = true) {
195 std::string res;
196 convert_(*this, type, simd, res);
197 return res;
198 }
199 std::string convert_ret_pi(const Pi*);
200
201 /// Registers @p arg as an incoming phi value for @p phi in @p callee, coming from predecessor @p pred.
202 void emit_phi(Lam* callee, const Def* phi, std::string arg, Lam* pred) {
203 lam2bb_[callee].phis[phi].emplace_back(std::move(arg), id(pred, true));
204 locals_[phi] = id(phi);
205 }
206
207 /// Wires all non-`mem.M` arguments of @p app into @p callee's phis, coming from predecessor @p pred.
208 void emit_phi_args(Lam* callee, const App* app, Lam* pred) {
209 size_t n = callee->num_tvars();
210 for (size_t i = 0; i != n; ++i)
211 if (auto arg = emit_unsafe(app->arg(n, i)); !arg.empty()) {
212 auto phi = callee->var(n, i);
213 if (Axm::isa<mem::M>(phi->type())) continue;
214 emit_phi(callee, phi, std::move(arg), pred);
215 }
216 }
217
218 /// Emits the storage backing a `mem.slot` of type @p pointee and yields the pointer value.
219 /// The generic backend allocates on the stack; targets may override (e.g. a global in a specific address space).
220 /// Kept inline on purpose so `Emitter` retains no vtable key function (else its vtable would live in a single
221 /// module and break derived backends loaded from a separate plugin).
222 virtual std::string emit_slot(BB& bb, const App* app, const Def* pointee, const Def* /*addr_space*/) {
223 auto v_ptr = "%" + app->unique_name() + ".slot";
224 std::print(bb.body().emplace_back(), "{} = alloca {}", v_ptr, convert(pointee, false));
225 return v_ptr;
226 }
227
228 absl::btree_set<std::string> decls_;
229 std::ostringstream type_decls_;
230 std::ostringstream vars_decls_;
231 std::ostringstream func_decls_;
232 std::ostringstream func_impls_;
234
235 /// Loop-metadata node id per `ll.vec`-annotated loop header (see `emit_epilogue_impl`);
236 /// numbered from `LoopMdBase + 1` to stay clear of the ids the embedded runtime module
237 /// brings along.
238 static constexpr u64 LoopMdBase = 1000;
240
242 bool rt_used_ = false;
243 std::string rt_module_;
244
245private:
246 // Real implementations; defined in ll.cpp and exported via the `mim_ll_*` shims above.
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*);
251
252 // Case groups of emit_bb_impl, split so one recursion level only pays the frame of the group it hits.
253 MIM_NOINLINE std::string emit_lit(const Def*);
254 MIM_NOINLINE std::string emit_tuple(BB&, const std::string& name, const Def* tuple);
255 MIM_NOINLINE std::pair<std::string, std::string> emit_gep_index(BB&, const std::string& name, const Def* index);
256 MIM_NOINLINE std::optional<std::string> emit_builtin(BB&, const std::string& name, const Def*);
257 MIM_NOINLINE std::optional<std::string> emit_core(BB&, const std::string& name, const Def*);
258 MIM_NOINLINE std::optional<std::string> emit_mem(BB&, const std::string& name, const Def*);
259 MIM_NOINLINE std::optional<std::string> emit_math(BB&, const std::string& name, const Def*);
260 MIM_NOINLINE std::optional<std::string> emit_vec(BB&, const std::string& name, const Def*);
261
262 decltype(&mim_ll_convert) convert_ = nullptr;
263 decltype(&mim_ll_finalize) finalize_ = nullptr;
264 decltype(&mim_ll_emit_epilogue) emit_epilogue_ = nullptr;
265 decltype(&mim_ll_emit_bb) emit_bb_ = nullptr;
266
267 friend void mim_ll_convert(Emitter&, const Def*, bool, std::string&);
268 friend void mim_ll_finalize(Emitter&);
269 friend void mim_ll_emit_epilogue(Emitter&, Lam*);
270 friend void mim_ll_emit_bb(Emitter&, BB&, const Def*, std::string&);
271};
272
273/*
274 * convert
275 */
276
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())) {
280 if (arr->body()->isa<Nat>() || Idx::isa(arr->body()) || Axm::isa<math::F>(arr->body()))
281 return std::pair{*l, arr->body()};
282 }
283 }
284 return {};
285}
286
287inline static std::optional<std::pair<nat_t, const Def*>> is_simd_aggregate(Defs types) {
288 if (std::ranges::all_of(types, [&](auto i) { return i == types[0]; })) {
289 if (types[0]->isa<Nat>() || Idx::isa(types[0]) || Axm::isa<math::F>(types[0]))
290 return std::pair{types.size(), types[0]};
291 }
292
293 return {};
294}
295
296inline static const Def* find_common_simd_src(const App* app) {
297 const Def* common_src = nullptr;
298 size_t lane = 0;
299 for (auto arg : app->args()) {
300 if (Axm::isa<mem::M>(arg->type())) continue;
301 auto extract = arg->isa<Extract>();
302 if (!extract || !is_simd(extract->tuple()->type())) return nullptr;
303 // Only devectorized args - lane i in position i - may forward the whole vector;
304 // anything else (e.g. a Select with a non-literal index) must stay scalar.
305 if (auto index = Lit::isa(extract->index()); !index || *index != lane++) return nullptr;
306 if (!common_src)
307 common_src = extract->tuple();
308 else if (common_src != extract->tuple())
309 return nullptr;
310 }
311 if (common_src) {
312 auto simd = is_simd(common_src->type());
313 if (!simd || simd->first != lane) return nullptr;
314 }
315 return common_src;
316}
317
318inline std::string Emitter::id(const Def* def, bool force_bb /*= false*/) const {
319 if (auto global = def->isa<Global>()) return "@" + global->unique_name();
320
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(); // TODO or use is_internal or sth like that?
325 return std::string("@") + lam->unique_name();
326 }
327 }
328
329 return std::string("%") + def->unique_name();
330}
331
332inline std::string Emitter::convert_ret_pi(const Pi* pi) {
333 auto dom = mem::strip_mem_ty(pi->dom());
334 if (dom == world().sigma()) return "void";
335 return convert(dom);
336}
337
338/*
339 * emit
340 */
341
342inline void Emitter::start() {
343 Super::start();
344
345 // Splice the runtime wrapper module first (it carries the module's target triple/datalayout).
346 if (rt_used_ && rt_ == Rt::embed) {
347 if (rt_module_.empty())
348 fe::throwf(MIM_LL_BE
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`)");
351 ostream() << rt_module_ << '\n';
352 }
353
354 ostream() << type_decls_.str() << '\n';
355 for (auto&& decl : decls_)
356 ostream() << decl << '\n';
357 ostream() << func_decls_.str() << '\n';
358 ostream() << vars_decls_.str() << '\n';
359 ostream() << func_impls_.str() << '\n';
360
361 // One distinct `!llvm.loop` node per hinted loop header, sharing the vectorize-enable hint.
362 if (!loop_md_.empty()) {
363 std::println(ostream(), "!{} = !{{!\"llvm.loop.vectorize.enable\", i1 true}}", LoopMdBase);
364 for (const auto& [_, md] : loop_md_)
365 std::println(ostream(), "!{} = distinct !{{!{}, !{}}}", md, md, LoopMdBase);
366 }
367}
368
369inline bool Emitter::load_rt_module(std::string_view filename) {
370 for (const auto& dir : world().driver().rt_paths()) {
371 auto path = dir / std::string(filename);
372 std::error_code ec;
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>());
377 return true;
378 }
379 }
380 return false;
381}
382
383inline void Emitter::emit_imported(Lam* lam) {
384 // TODO merge with declare method
385 std::print(func_decls_, "declare {} {}(", convert_ret_pi(lam->type()->ret_pi()), id(lam));
386
387 auto doms = lam->doms();
388 for (auto sep = ""; auto dom : doms.view().rsubspan(1)) {
389 if (Axm::isa<mem::M>(dom)) continue;
390 std::print(func_decls_, "{}{}", sep, convert(dom));
391 sep = ", ";
392 }
393
394 std::print(func_decls_, ")\n");
395}
396
397inline std::string Emitter::prepare() {
398 auto internal = root()->is_external() ? "" : "internal ";
399 auto ret_t = convert_ret_pi(root()->type()->ret_pi());
400 std::print(func_impls_, "define {} {} {}(", internal, ret_t, id(root()));
401
402 auto vars = root()->vars();
403 for (auto sep = ""; auto var : vars.view().rsubspan(1)) {
404 if (Axm::isa<mem::M>(var->type())) continue;
405 if (auto sigma = var->type()->isa<Sigma>(); sigma && sigma->num_ops() == 0) continue;
406 if (auto arr = var->type()->isa<Arr>())
407 if (is_simd(arr->body())) convert(arr->body()); // pre-add input vector to cache
408 auto name = id(var);
409 locals_[var] = name;
410 std::print(func_impls_, "{}{} {}", sep, convert(var->type()), name);
411 sep = ", ";
412 }
413
414 std::print(func_impls_, ") {{\n");
415 return root()->unique_name();
416}
417
418} // namespace plug::ll
419} // namespace mim
const Def * arg() const
Definition lam.h:284
A (possibly paramterized) Array.
Definition tuple.h:110
static auto isa(const Def *def)
Definition axm.h:112
Lam * root() const
Definition phase.h:624
Base class for all Defs.
Definition def.h:273
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:580
const Def * op(size_t i) const noexcept
Definition def.h:351
const Def * var(nat_t a, nat_t i) noexcept
Definition def.h:479
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.h:1111
nat_t num_tvars() noexcept
Definition def.h:479
bool is_external() const noexcept
Definition def.h:553
auto vars(F f) noexcept
Definition def.h:479
std::string unique_name() const
name + "_" + Def::gid
Definition def.cpp:616
constexpr size_t num_ops() const noexcept
Definition def.h:352
void load(std::string_view name)
Definition driver.cpp:126
bool is_loaded(std::string_view name) const
Definition driver.h:177
Extracts from a Sigma or Array-typed Extract::tuple the element at position Extract::index.
Definition tuple.h:161
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
Definition def.cpp:645
A function.
Definition lam.h:113
const Pi * type() const
Definition lam.h:133
static std::optional< T > isa(const Def *def)
Definition def.h:937
Driver & driver()
Definition phase.h:78
const fe::Vector< std::string > & args()
Command-line arguments passed to this Phase's plugin via -X <plugin>:<arg>.
Definition phase.cpp:23
std::string_view name() const
Definition phase.h:80
virtual void start()=0
Actual entry.
World & world()
Definition phase.h:77
A dependent function type.
Definition lam.h:14
const Def * dom() const
Definition lam.h:35
const Pi * ret_pi() const
Yields the last Pi::dom, if Pi::isa_basicblock.
Definition lam.cpp:13
A dependent tuple type.
Definition tuple.h:23
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:40
Driver & driver()
Definition world.h:103
const fe::Log & log() const
Log via log().e("...", args) etc.; owned by the Driver.
Definition world.cpp:129
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.
Definition ll.h:208
void rt_module(std::string ll)
Provides the textual LLVM IR of the runtime module to splice in Rt::embed mode.
Definition ll.h:172
virtual std::string prepare()
Definition ll.h:397
Emitter(World &world, std::string name, std::ostream &ostream)
Definition ll.h:128
virtual std::optional< std::string > isa_targetspecific_intrinsic(BB &, const Def *)
Definition ll.h:154
void emit_imported(Lam *)
Definition ll.h:383
static constexpr u64 LoopMdBase
Loop-metadata node id per ll.vec-annotated loop header (see emit_epilogue_impl); numbered from LoopMd...
Definition ll.h:238
absl::btree_set< std::string > decls_
Definition ll.h:228
bool is_valid(std::string_view s)
Definition ll.h:140
void rt_mode(Rt rt)
Definition ll.h:170
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....
Definition ll.cpp:1134
std::string convert_ret_pi(const Pi *)
Definition ll.h:332
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.
Definition ll.h:202
std::string rt_module_
Definition ll.h:243
void declare(std::format_string< Args... > s, Args &&... args)
Definition ll.h:157
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,...
Definition ll.h:369
std::ostringstream func_decls_
Definition ll.h:231
std::string id(const Def *, bool force_bb=false) const
Definition ll.h:318
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...
Definition ll.h:187
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.
Definition ll.h:222
std::ostringstream vars_decls_
Definition ll.h:230
std::ostringstream func_impls_
Definition ll.h:232
Rt
How the C runtime wrappers (compiled to a <name>.ll via add_mim_runtime) reach the output.
Definition ll.h:165
@ embed
Splice the wrapper IR into the emitted module so it is self-contained.
Definition ll.h:166
@ ext
Only declare the wrappers; the runtime is linked in externally.
Definition ll.h:167
mim::Emitter< std::string, std::string, BB, Emitter > Super
Definition ll.h:126
void start() override
Actual entry.
Definition ll.h:342
virtual void emit_epilogue(Lam *lam)
Definition ll.h:146
LamMap< u64 > loop_md_
Definition ll.h:239
friend void mim_ll_finalize(Emitter &)
Definition ll.cpp:1137
virtual std::string convert(const Def *type, bool simd=true)
Definition ll.h:194
std::string emit_bb(BB &bb, const Def *def)
Definition ll.h:148
std::ostringstream type_decls_
Definition ll.h:229
LamMap< const Def * > simd_phi_
Definition ll.h:233
friend void mim_ll_emit_bb(Emitter &, BB &, const Def *, std::string &)
Definition ll.cpp:1139
friend void mim_ll_emit_epilogue(Emitter &, Lam *)
Definition ll.cpp:1138
#define MIM_NOINLINE
Definition config.h:23
#define MIM_EXPORT
Definition config.h:21
#define MIM_LL_BE
Prefix for this backend's fe::throwf messages; concatenate it with the format literal.
Definition ll.h:43
The ll Plugin
Definition ll.h:39
static std::optional< std::pair< nat_t, const Def * > > is_simd(const Def *type)
Definition ll.h:277
void mim_ll_finalize(Emitter &)
Definition ll.cpp:1137
void mim_ll_emit_epilogue(Emitter &, Lam *)
Definition ll.cpp:1138
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....
Definition ll.cpp:1134
static const Def * find_common_simd_src(const App *app)
Definition ll.h:296
static std::optional< std::pair< nat_t, const Def * > > is_simd_aggregate(Defs types)
Definition ll.h:287
void mim_ll_emit_bb(Emitter &, BB &, const Def *, std::string &res)
Definition ll.cpp:1139
The math Plugin
Definition math.h:8
std::optional< nat_t > isa_f(const Def *def)
Definition math.h:77
The mem Plugin
Definition mem.h:11
const Def * strip_mem_ty(const Def *def)
Removes recusively all occurences of mem from a type (sigma).
Definition mem.h:58
The tuple Plugin
Definition ast.h:16
fe::View< const Def * > Defs
Definition def.h:91
GIDMap< const Def *, To > DefMap
Definition def.h:88
GIDMap< Lam *, To > LamMap
Definition lam.h:219
uint64_t u64
Definition types.h:27
std::deque< std::ostringstream > & tail()
Definition ll.h:87
std::deque< std::ostringstream > & body()
Definition ll.h:86
DefMap< std::deque< std::pair< std::string, std::string > > > phis
Definition ll.h:108
BB & operator=(BB other) noexcept
Definition ll.h:83
BB(BB &&other) noexcept=default
std::array< std::deque< std::ostringstream >, 3 > parts
Definition ll.h:109
friend void swap(BB &a, BB &b) noexcept
Definition ll.h:102
std::deque< std::ostringstream > & head()
Definition ll.h:85
void tail(std::format_string< Args... > s, Args &&... args)
Definition ll.h:98
std::string assign(std::string_view name, std::format_string< Args... > s, Args &&... args)
Definition ll.h:90
BB(const BB &)=delete