MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
plugin.h
Go to the documentation of this file.
1#pragma once
2
3#include <compare>
4
5#include <fstream>
6#include <functional>
7#include <initializer_list>
8#include <iostream>
9#include <memory>
10#include <optional>
11#include <string>
12#include <string_view>
13#include <tuple>
14
15#include <ankerl/unordered_dense.h>
16
17#include "mim/config.h"
18#include "mim/def.h"
19
20namespace mim {
21
22class Driver;
23class Phase;
24
25/// @name Plugin Interface
26///@{
27using Normalizers = ankerl::unordered_dense::map<flags_t, NormalizeFn>;
28
29/// Maps an axiom of a Phase to a function that creates one.
30using Flags2Phases = ankerl::unordered_dense::map<flags_t, std::function<std::unique_ptr<Phase>(World&)>>;
31
32/// One `-X <plugin>:<arg>` a Plugin understands; see @ref clipluginargs.
33/// A Plugin declares these next to the code that picks them apart, so that `mim -p <plugin> -h` can list them.
34struct PluginArg {
35 const char* syntax; ///< How to spell the argument, e.g. `"o=<file>, output=<file>"`.
36 const char* descr; ///< What it does; one sentence of Markdown, but no links: the terminal help prints it as is.
37};
38
39/// One environment variable a Plugin reads; see @ref clipluginenv.
40/// A Plugin declares these next to the code that reads them, so that `mim -p <plugin> -h` can list them.
41struct PluginEnv {
42 const char* name; ///< Name of the variable, e.g. `"CUDA_HOME"`.
43 const char* descr; ///< What it does; one sentence of Markdown, but no links: the terminal help prints it as is.
44};
45
46/// One symbol a Plugin offers to other Plugin%s; see Driver::get_fun_ptr.
47/// A statically linked Plugin has no shared object to look it up in.
48struct PluginSym {
49 const char* name; ///< Name of the symbol, e.g. `"mim_ll_convert"`.
50 void* ptr; ///< Address of the symbol.
51};
52
53/// Builds a PluginSym for @p f, so name and address cannot drift apart.
54#define MIM_PLUGIN_SYM(f) \
55 PluginSym { #f, (void*)&f }
56///@}
57
58/// @name Plugin Argument Lookup
59/// Picks the `-X <plugin>:<arg>` strings of Driver::args / Phase::args apart.
60/// Each helper matches any of @p keys - `arg_value(args(), "o", "output")` - and the last occurrence wins.
61///@{
62namespace detail {
63/// `<key>` ↦ `""`, `<key>=<value>` ↦ `<value>`, anything else ↦ `std::nullopt`.
64inline std::optional<std::string_view> arg_split(std::string_view arg, std::string_view key) {
65 if (!arg.starts_with(key)) return {};
66 auto val = arg.substr(key.size());
67 if (val.empty()) return val;
68 if (val.front() == '=') return val.substr(1);
69 return {};
70}
71} // namespace detail
72
73/// Value of `<key>=<value>`; `std::nullopt` if none of @p keys carries one.
74template<class... Keys>
75std::optional<std::string_view> arg_value(fe::View<std::string> args, Keys... keys) {
76 std::optional<std::string_view> res;
77 for (std::string_view arg : args)
78 for (std::string_view key : {std::string_view(keys)...})
79 if (auto val = detail::arg_split(arg, key); val && !val->empty()) res = val;
80 return res;
81}
82
83/// An @p on key ↦ `true`, an @p off key ↦ `false`; `std::nullopt` if neither occurs.
84inline std::optional<bool> arg_bool(fe::View<std::string> args,
85 std::initializer_list<std::string_view> on,
86 std::initializer_list<std::string_view> off) {
87 std::optional<bool> res;
88 for (std::string_view arg : args) {
89 for (auto key : on)
90 if (arg == key) res = true;
91 for (auto key : off)
92 if (arg == key) res = false;
93 }
94 return res;
95}
96
97/// Whether any of @p keys occurs.
98template<class... Keys>
99bool arg_flag(fe::View<std::string> args, Keys... keys) {
100 for (std::string_view arg : args)
101 for (std::string_view key : {std::string_view(keys)...})
102 if (arg == key) return true;
103 return false;
104}
105
106///@}
107
108/// A file name from the command line and the stream to write to; @see arg_value.
109class Out {
110public:
111 Out() = default;
112 explicit Out(std::string name)
113 : name_(std::move(name)) {}
114
115 std::string& name() { return name_; } ///< Bound to a `fe::Cli` option, e.g. `--output-mim`.
116
117 /// The stream to write to; `nullptr` if this output was not requested, `std::cout` for `"-"`.
118 /// Opens the file upon first use, so an output no one writes to leaves no file behind.
119 std::ostream* os() {
120 if (name_.empty()) return nullptr;
121 if (name_ == "-") return &std::cout;
122 if (!ofs_.is_open()) {
123 ofs_.open(name_);
124 if (!ofs_) fe::throwf("cannot open output file `{}`", name_);
125 }
126 return &ofs_;
127 }
128
129private:
130 std::string name_;
131 std::ofstream ofs_;
132};
133
134struct Version {
135 int major;
136 int minor;
137 const char* suffix;
138 const char* hash;
139
140 /// Compares major/minor/suffix, ignores hash.
141 constexpr auto operator<=>(const Version& other) const noexcept {
142 auto cmp = std::tie(major, minor) <=> std::tie(other.major, other.minor);
143 if (cmp != 0) return cmp;
144
145 return std::strcmp(suffix, other.suffix) <=> 0;
146 }
147
148 /// Compares major/minor/suffix, ignores hash.
149 constexpr bool operator==(const Version& other) const noexcept {
150 return major == other.major && minor == other.minor && std::strcmp(suffix, other.suffix) == 0;
151 }
152
153 friend std::ostream& operator<<(std::ostream& os, const Version& v) {
154 return os << v.major << '.' << v.minor << v.suffix << " (" << v.hash << ")";
155 }
156};
157
158extern "C" {
159
160#define MIM_VERSION \
161 Version { MIM_VER_MAJOR, MIM_VER_MINOR, MIM_VER_SUFFIX, MIM_GIT_HASH }
162
163/// Basic info and registration function pointer to be returned from a specific plugin.
164/// Use Driver to load such a plugin.
165struct Plugin {
166 using Handle = std::unique_ptr<void, void (*)(void*)>;
167
168 const char* name; ///< Name of the Plugin.
169 Version version; ///< Version of the Plugin.
170
171 /// Callback for registering the mapping from axm ids to normalizer functions in the given @p normalizers map.
173 /// Callback for registering the Plugin's callbacks for Phase%s.
175
176 // No default member initializers, and hence no designated ones either: clang's
177 // -Wreturn-type-c-linkage only accepts a POD as an `extern "C"` return type.
178 // MIM_PLUGIN_ENTRY hands out a zeroed Plugin instead.
179 const PluginArg* args; ///< The `-X` arguments this Plugin understands; see PluginArg.
180 size_t num_args; ///< Number of Plugin::args.
181 const PluginEnv* envs; ///< The environment variables this Plugin reads; see PluginEnv.
182 size_t num_envs; ///< Number of Plugin::envs.
183 const PluginSym* syms; ///< The symbols other Plugin%s may look up; see PluginSym.
184 size_t num_syms; ///< Number of Plugin::syms.
185};
186
187/// @name Plugin Interface
188/// @see Plugin
189///@{
190/// To be implemented and exported by a plugin.
191/// @returns a filled Plugin.
193///@}
194}
195
196#ifdef MIM_STATIC_PLUGINS
197# define MIM_PLUGIN_ENTRY_NAME(p) mim_get_plugin_##p
198#else
199# define MIM_PLUGIN_ENTRY_NAME(p) mim_get_plugin
200#endif
201
202/// Defines a Plugin's entry point; the body fills in the `plugin` handed to it, as in
203/// `MIM_PLUGIN_ENTRY(demo) { plugin.register_normalizers = demo::register_normalizers; }`.
204/// Plugin::name and Plugin::version are already set, and every other field is zeroed.
205/// @p p must be the Plugin's name: a `MIM_STATIC_PLUGINS` build needs one entry point per Plugin.
206#define MIM_PLUGIN_ENTRY(p) \
207 static void mim_plugin_##p(mim::Plugin&); \
208 extern "C" MIM_EXPORT mim::Plugin MIM_PLUGIN_ENTRY_NAME(p)() { \
209 auto plugin = mim::Plugin{}; \
210 plugin.name = #p; \
211 plugin.version = MIM_VERSION; \
212 mim_plugin_##p(plugin); \
213 return plugin; \
214 } \
215 static void mim_plugin_##p([[maybe_unused]] mim::Plugin& plugin)
216
217/// Holds info about an entity defined within a Plugin (called *Annex*).
218struct Annex {
219 Annex() = delete;
220
221 /// @name Mangling Plugin Name
222 ///@{
223 static constexpr size_t Max_Plugin_Size = 8;
224 static constexpr plugin_t Global_Plugin = 0xffff'ffff'ffff'0000_u64;
225
226 /// Mangles @p s into a dense 48-bit representation.
227 /// The layout is as follows:
228 /// ```
229 /// |---7--||---6--||---5--||---4--||---3--||---2--||---1--||---0--|
230 /// 7654321076543210765432107654321076543210765432107654321076543210
231 /// Char67Char66Char65Char64Char63Char62Char61Char60|---reserved---|
232 /// ```
233 /// The `reserved` part is used for the Axm::tag and the Axm::sub.
234 /// Each `Char6x` is 6-bit wide and hence a plugin name has at most Axm::Max_Plugin_Size = 8 chars.
235 /// It uses this encoding:
236 /// | `Char6` | ASCII |
237 /// |---------|---------|
238 /// | 1: | `_` |
239 /// | 2-27: | `a`-`z` |
240 /// | 28-53: | `A`-`Z` |
241 /// | 54-63: | `0`-`9` |
242 /// The 0 is special and marks the end of the name if the name has less than 8 chars.
243 /// @returns `std::nullopt` if encoding is not possible.
244 static std::optional<plugin_t> mangle(std::string_view plugin);
245
246 /// Reverts an Axm::mangle%d @p plugin back to its name; never longer than Annex::Max_Plugin_Size.
247 /// Ignores lower 16-bit of @p plugin.
248 static std::string demangle(plugin_t plugin);
249
250 ///@}
251
252 /// @name Annex Name
253 /// @anchor annex_name
254 /// Anatomy of an Annex name:
255 /// ```
256 /// plugin.tag.sub
257 /// | 48 | 8 | 8 | <-- Number of bits per field.
258 /// ```
259 /// * Def::name() retrieves the full name as Sym.
260 /// * Def::flags() retrieves the full name as Axm::mangle%d 64-bit integer.
261 ///@{
262 /// Yields the `plugin` part of the name as integer.
263 /// It consists of 48 relevant bits that are returned in the highest 6 bytes of a 64-bit integer.
264 static constexpr plugin_t flags2plugin(flags_t f) { return f & Global_Plugin; }
265
266 /// Yields the `tag` part of the name as integer.
267 static constexpr tag_t flags2tag(flags_t f) { return tag_t((f & 0x0000'0000'0000'ff00_u64) >> 8_u64); }
268
269 /// Yields the `sub` part of the name as integer.
270 static constexpr sub_t flags2sub(flags_t f) { return sub_t(f & 0x0000'0000'0000'00ff_u64); }
271
272 /// Includes Axm::plugin() and Axm::tag() but **not** Axm::sub.
273 static constexpr flags_t flags2base(flags_t f) { return f & ~0xff_u64; }
274
275 /// Assembles the full flags from its `plugin`, `tag`, and `sub` fields.
276 static constexpr flags_t flags(plugin_t p, tag_t t, sub_t s = 0) { return p | (flags_t(t) << 8_u64) | flags_t(s); }
277 ///@}
278
279 /// @name Helpers for Matching
280 /// These are set via template specialization.
281 ///@{
282 // clang-format off
283 template<class Id> static constexpr size_t Num = size_t(-1); ///< Number of Axm::sub%tags.
284 template<class Id> static constexpr flags_t Base = flags_t(-1); ///< @see Axm::base.
285 template<class Id> static consteval size_t num () { return Num <Id>; }
286 template<class Id> static consteval flags_t base() { return Base<Id>; }
287 // clang-format of
288 ///@}
289};
290
291} // namespace mim
292
293#ifndef DOXYGEN
294template<> struct std::formatter<mim::Version> : fe::ostream_formatter {};
295#endif
Some "global" variables needed all over the place.
Definition driver.h:64
Out()=default
Out(std::string name)
Definition plugin.h:112
std::string & name()
Bound to a fe::Cli option, e.g. --output-mim.
Definition plugin.h:115
std::ostream * os()
The stream to write to; nullptr if this output was not requested, std::cout for "-".
Definition plugin.h:119
A Phase performs one self-contained task over the whole World.
Definition phase.h:25
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:43
#define MIM_EXPORT
Definition config.h:21
Definition ast.h:16
const char * descr
What it does; one sentence of Markdown, but no links: the terminal help prints it as is.
Definition plugin.h:36
bool arg_flag(fe::View< std::string > args, Keys... keys)
Whether any of keys occurs.
Definition plugin.h:99
u8 sub_t
Definition types.h:42
u64 flags_t
Definition types.h:39
ankerl::unordered_dense::map< flags_t, NormalizeFn > Normalizers
Definition plugin.h:27
mim::Plugin mim_get_plugin()
const char * name
Name of the symbol, e.g. "mim_ll_convert".
Definition plugin.h:49
u64 plugin_t
Definition types.h:40
fe::SymTab< ast::Tok::Tag, ast::Num_Keys+ast::Num_Subst > Keys
The reserved words the ast::Lexer looks up, keyed by the Sym it has just interned.
Definition driver.h:28
const char * descr
What it does; one sentence of Markdown, but no links: the terminal help prints it as is.
Definition plugin.h:43
const char * name
Name of the variable, e.g. "CUDA_HOME".
Definition plugin.h:42
void * ptr
Address of the symbol.
Definition plugin.h:50
const char * syntax
How to spell the argument, e.g. "o=<file>, output=<file>".
Definition plugin.h:35
std::optional< bool > arg_bool(fe::View< std::string > args, std::initializer_list< std::string_view > on, std::initializer_list< std::string_view > off)
An on key ↦ true, an off key ↦ false; std::nullopt if neither occurs.
Definition plugin.h:84
u8 tag_t
Definition types.h:41
std::optional< std::string_view > arg_value(fe::View< std::string > args, Keys... keys)
Value of <key>=<value>; std::nullopt if none of keys carries one.
Definition plugin.h:75
ankerl::unordered_dense::map< flags_t, std::function< std::unique_ptr< Phase >(World &)> > Flags2Phases
Maps an axiom of a Phase to a function that creates one.
Definition plugin.h:30
One -X <plugin>:<arg> a Plugin understands; see Arguments.
Definition plugin.h:34
One environment variable a Plugin reads; see Environment Variables.
Definition plugin.h:41
One symbol a Plugin offers to other Plugins; see Driver::get_fun_ptr.
Definition plugin.h:48
static constexpr flags_t flags(plugin_t p, tag_t t, sub_t s=0)
Assembles the full flags from its plugin, tag, and sub fields.
Definition plugin.h:276
Annex()=delete
static constexpr plugin_t Global_Plugin
Definition plugin.h:224
static std::string demangle(plugin_t plugin)
Reverts an Axm::mangled plugin back to its name; never longer than Annex::Max_Plugin_Size.
Definition plugin.cpp:33
static std::optional< plugin_t > mangle(std::string_view plugin)
Mangles s into a dense 48-bit representation.
Definition plugin.cpp:5
static constexpr tag_t flags2tag(flags_t f)
Yields the tag part of the name as integer.
Definition plugin.h:267
static constexpr size_t Max_Plugin_Size
Definition plugin.h:223
static constexpr sub_t flags2sub(flags_t f)
Yields the sub part of the name as integer.
Definition plugin.h:270
static constexpr plugin_t flags2plugin(flags_t f)
Definition plugin.h:264
static consteval size_t num()
Definition plugin.h:285
static constexpr size_t Num
Number of Axm::subtags.
Definition plugin.h:283
static consteval flags_t base()
Definition plugin.h:286
static constexpr flags_t Base
Definition plugin.h:284
static constexpr flags_t flags2base(flags_t f)
Includes Axm::plugin() and Axm::tag() but not Axm::sub.
Definition plugin.h:273
Basic info and registration function pointer to be returned from a specific plugin.
Definition plugin.h:165
const PluginEnv * envs
The environment variables this Plugin reads; see PluginEnv.
Definition plugin.h:181
size_t num_syms
Number of Plugin::syms.
Definition plugin.h:184
size_t num_envs
Number of Plugin::envs.
Definition plugin.h:182
size_t num_args
Number of Plugin::args.
Definition plugin.h:180
const PluginSym * syms
The symbols other Plugins may look up; see PluginSym.
Definition plugin.h:183
const PluginArg * args
The -X arguments this Plugin understands; see PluginArg.
Definition plugin.h:179
void(*) register_normalizers(Normalizers &)
Callback for registering the mapping from axm ids to normalizer functions in the given normalizers ma...
Definition plugin.h:172
std::unique_ptr< void, void(*)(void *)> Handle
Definition plugin.h:166
const char * name
Name of the Plugin.
Definition plugin.h:168
void(*) register_phases(Flags2Phases &)
Callback for registering the Plugin's callbacks for Phases.
Definition plugin.h:174
Version version
Version of the Plugin.
Definition plugin.h:169
friend std::ostream & operator<<(std::ostream &os, const Version &v)
Definition plugin.h:153
constexpr bool operator==(const Version &other) const noexcept
Compares major/minor/suffix, ignores hash.
Definition plugin.h:149
constexpr auto operator<=>(const Version &other) const noexcept
Compares major/minor/suffix, ignores hash.
Definition plugin.h:141
const char * hash
Definition plugin.h:138
const char * suffix
Definition plugin.h:137