MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
driver.cpp
Go to the documentation of this file.
1#include "mim/driver.h"
2
3#include <algorithm>
4#include <filesystem>
5#include <iostream>
6#include <optional>
7#include <sstream>
8#include <stdexcept>
9
10#include <fe/dl.h>
11#include <fe/sys.h>
12
13#include "mim/config.h"
14#include "mim/plugin.h"
15
16// Any address inside libmim identifies the shared object it was loaded from; see path_to_libmim.
17extern "C" MIM_EXPORT void mim_lib_anchor() {}
18
19namespace mim {
20
21namespace {
22
23/// Install tree @p libmim_path belongs to, i.e. the directory whose `<MIM_LIBDIR>/mim` holds the plugins.
24std::optional<fs::path> prefix_of(const fs::path& libmim_path) {
25 for (auto dir = libmim_path.parent_path(); !dir.empty(); dir = dir.parent_path()) {
26 std::error_code ignore;
27 if (fs::is_directory(dir / MIM_LIBDIR / "mim", ignore) && !ignore) return dir;
28 if (dir == dir.root_path()) break;
29 }
30
31 return {};
32}
33
34std::optional<fs::path> path_to_libmim() { return fe::sys::path_to_lib((const void*)&mim_lib_anchor); }
35
36/// A prefix may derive what a plain directory already names, and probing it twice only slows lookup down.
37void push(fe::Vector<fs::path>& paths, fs::path path) {
38 if (std::ranges::find(paths, path) == paths.end()) paths.emplace_back(std::move(path));
39}
40
41} // namespace
42
43void Driver::Imports::add(const fe::Src* src, Sym sym, ast::Tok::Tag tag, bool is_path) {
44 // The SrcMap interns paths, so one file is one fe::Src - comparing those settles "same file".
45 // Aliases (`as`) must not add a second entry, so the spelling is not part of the key.
46 for (const auto& entry : entries_)
47 if (entry.tag == tag && entry.src == src) return;
48
49 entries_.emplace_back(Entry{src, sym, tag, is_path});
50}
51
52Driver::Driver(std::string name)
53 : fe::Driver(std::make_unique<Diag>(*this))
54 , version_(MIM_VERSION)
55 , world_(this, sym(name)) {
56#define CODE(t, str) keys_.emplace(sym(str), ast::Tok::Tag::t);
58#undef CODE
59
60#define CODE(str, t) \
61 if (ast::Tok::Tag::t != ast::Tok::Tag::Nil) keys_.emplace(sym(str), ast::Tok::Tag::t);
63#undef CODE
64
65 auto from_env = [](const char* var, auto&& add) {
66 if (auto env = std::getenv(var)) {
67 auto stream = std::stringstream{env};
68 auto path = std::string{};
69 while (std::getline(stream, path, fe::sys::Path_Sep))
70 add(fs::path{path});
71 }
72 };
73
74 from_env("MIM_PLUGIN_PATH", [this](fs::path path) { add_plugin_path(std::move(path)); });
75 from_env("MIM_IMPORT_PATH", [this](fs::path path) { add_import_path(std::move(path)); });
76 from_env("MIM_PREFIX_PATH", [this](fs::path path) { add_prefix_path(std::move(path)); });
77
78 if (auto path = path_to_libmim()) {
79 // A layout that keeps the plugins right next to libmim has no prefix to derive them from.
80 add_plugin_path(path->parent_path() / "mim");
81 if (auto prefix = prefix_of(*path)) add_prefix_path(*std::move(prefix));
82 }
83
85
86 // User paths are added later and must be searched before the environment and derived ones.
87 plugin_dirs_.seal();
88 import_dirs_.seal();
89 prefixes_.seal();
90}
91
92fe::Vector<fs::path> Driver::plugin_paths() const {
93 auto res = fe::Vector<fs::path>();
94 res.emplace_back();
95 for (const auto& dir : plugin_dirs_)
96 push(res, dir);
97 for (const auto& prefix : prefixes_)
98 push(res, prefix / MIM_LIBDIR / "mim");
99 return res;
100}
101
102fe::Vector<fs::path> Driver::import_paths() const {
103 auto res = fe::Vector<fs::path>();
104 res.emplace_back();
105 for (const auto& dir : import_dirs_)
106 push(res, dir);
107 for (const auto& dir : plugin_dirs_)
108 push(res, dir);
109 for (const auto& prefix : prefixes_) {
110 push(res, prefix / MIM_DATADIR / "mim");
111 push(res, prefix / MIM_LIBDIR / "mim");
112 }
113 return res;
114}
115
116fe::Vector<fs::path> Driver::rt_paths() const {
117 auto res = fe::Vector<fs::path>();
118 res.emplace_back("rt");
119 for (const auto& dir : plugin_dirs_)
120 push(res, dir / "rt");
121 for (const auto& prefix : prefixes_)
122 push(res, prefix / MIM_LIBDIR / "mim" / "rt");
123 return res;
124}
125
126void Driver::load(std::string_view name) {
127 log().i("💾 load plugin `{}`", name);
128
129 if (is_loaded(name)) {
130 log().w("plugin `{}` already loaded", name);
131 return;
132 }
133
134 auto handle = Plugin::Handle{nullptr, fe::dl::close};
135 auto dir = fs::path{};
136 if (auto path = fs::path{name}; path.is_absolute() && fs::is_regular_file(path)) {
137 auto path_str = path.string();
138 if (handle.reset(fe::dl::open(path_str.c_str())); handle) dir = path.parent_path();
139 }
140 if (!handle) {
141 for (const auto& path : plugin_paths()) {
142 auto full_path = path / std::format("libmim_{}.{}", name, fe::dl::Ext);
143 std::error_code ignore;
144 if (bool reg_file = fs::is_regular_file(full_path, ignore); reg_file && !ignore) {
145 auto path_str = full_path.string();
146 if (handle.reset(fe::dl::open(path_str.c_str())); handle) {
147 dir = path;
148 break;
149 }
150 }
151 }
152 }
153
154 if (!handle) fe::throwf("cannot open plugin `{}`", name);
155
156 if (auto get_info = reinterpret_cast<decltype(&mim_get_plugin)>(fe::dl::get(handle.get(), "mim_get_plugin"))) {
157 auto plugin = get_info();
158 if (version() != plugin.version) {
159 std::ostringstream oss;
160 std::print(oss, "plugin {} has version {} while MimIR has version {}", plugin.name, plugin.version,
161 version());
162 if (flags().force_load)
163 std::cerr << "warning: " << oss.str() << '\n';
164 else
165 throw std::logic_error(oss.str());
166 }
167 fe::assert_emplace(plugins_, std::string(name), std::move(handle));
168 plugin2dir_.emplace(std::string(name), std::move(dir));
169 // clang-format off
170 if (auto reg = plugin.register_normalizers) reg(normalizers_);
171 if (auto reg = plugin.register_phases) reg(phases_);
172 // clang-format on
173 if (plugin.args) known_args_.emplace_back(name, fe::View<PluginArg>(plugin.args, plugin.num_args));
174 if (plugin.envs) known_envs_.emplace_back(name, fe::View<PluginEnv>(plugin.envs, plugin.num_envs));
175 } else {
176 fe::throwf("plugin `{}` has no `mim_get_plugin()`", name);
177 }
178}
179
180void* Driver::get_fun_ptr(std::string_view plugin, const char* name) {
181 if (auto handle = fe::lookup(plugins_, plugin)) return fe::dl::get(handle->get(), name);
182 return nullptr;
183}
184
185const fe::Vector<std::string>& Driver::args(std::string_view plugin) const {
186 static const fe::Vector<std::string> empty;
187 if (auto i = plugin_args_.find(plugin); i != plugin_args_.end()) return i->second;
188 return empty;
189}
190
192 : driver_(driver) {
193 if (!driver_) return;
194
195 auto& names = driver_->names();
196 if (names.depth++ == 0) {
197 names.clashed = false;
198 names.sym2gid.clear();
199 }
200}
201
203 if (driver_) --driver_->names().depth;
204}
205
206bool PlainNames::clashed() const { return driver_ && driver_->names().clashed; }
207
208bool PlainNames::claim(const Driver& driver, Sym sym, u32 gid) {
209 auto& names = driver.names();
210 if (names.depth == 0) return false;
211 if (auto [i, ins] = names.sym2gid.emplace(sym, gid); !ins && i->second != gid) names.clashed = true;
212 return true;
213}
214
215std::string Diag::render(const std::function<std::string()>& fmt) const {
216 bool clashed = false;
217 auto str = std::string();
218 {
219 auto plain = PlainNames(&driver_);
220 str = CodeDiag::render(fmt);
221 clashed = plain.clashed();
222 }
223 return clashed ? CodeDiag::render(fmt) : str; // the retry must run outside the guard, or it renders plainly again
224}
225
226} // namespace mim
Renders a diagnostic through PlainNames and - if that turned out ambiguous - once more with Def::uniq...
Definition driver.h:50
std::string render(const std::function< std::string()> &) const override
Definition driver.cpp:215
void add(const fe::Src *src, Sym, ast::Tok::Tag, bool path)
Remembers the directive that pulled in src; a repeated import of the same file adds nothing.
Definition driver.cpp:43
Some "global" variables needed all over the place.
Definition driver.h:63
void add_prefix_path(fs::path path)
Definition driver.h:126
Driver(std::string name={})
Definition driver.cpp:52
void add_import_path(fs::path path)
Definition driver.h:125
void load(std::string_view name)
Definition driver.cpp:126
const Version & version() const
MimIR Version.
Definition driver.h:83
fe::Vector< fs::path > import_paths() const
Where ast::Parser looks for <name>.mim; plugin directories are included, as a plugin ships both halve...
Definition driver.cpp:102
bool is_loaded(std::string_view name) const
Definition driver.h:177
Names & names() const
Definition driver.h:97
fe::Vector< fs::path > rt_paths() const
Where a backend looks for its runtime modules.
Definition driver.cpp:116
Flags & flags()
Definition driver.h:76
const fe::Vector< std::string > & args(std::string_view plugin) const
Yields an empty fe::Vector if plugin has none.
Definition driver.cpp:185
void add_plugin_path(fs::path path)
Definition driver.h:124
fe::Vector< fs::path > plugin_paths() const
Where Driver::load looks for libmim_<name>.
Definition driver.cpp:92
void * get_fun_ptr(std::string_view plugin, const char *name)
Definition driver.cpp:180
fe::Log & log()
Definition driver.h:78
PlainNames(const Driver *driver)
Activates plain naming on driver until this guard dies; a null driver leaves it off.
Definition driver.cpp:191
bool clashed() const
Definition driver.cpp:206
static bool claim(const Driver &, Sym sym, uint32_t gid)
Registers that gid renders as sym and reports whether the plain sym may be used.
Definition driver.cpp:208
#define MIM_DATADIR
Definition config.h:14
#define MIM_LIBDIR
Definition config.h:13
#define MIM_EXPORT
Definition config.h:21
#define MIM_INSTALL_PREFIX
Definition config.h:12
void mim_lib_anchor()
Definition driver.cpp:17
Definition ast.h:16
mim::Plugin mim_get_plugin()
uint32_t u32
Definition types.h:27
#define MIM_VERSION
Definition plugin.h:149
std::unique_ptr< void, void(*)(void *)> Handle
Definition plugin.h:155
#define CODE(name,...)
Definition tok.h:51
#define MIM_SUBST(m)
Definition tok.h:216
#define MIM_KEY(m)
Definition tok.h:75