MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1#include <cstdlib>
2#include <cstring>
3
4#include <fstream>
5#include <string>
6
7#include <fe/term.h>
8#include <lyra/lyra.hpp>
9
10#include "mim/config.h"
11#include "mim/driver.h"
12#include "mim/flags.h"
13#include "mim/phase.h"
14#include "mim/sexpr.h"
15
16#include "mim/ast/parser.h"
17#include "mim/phase/optimize.h"
18#include "mim/util/sys.h"
19
20using namespace mim;
21using namespace std::literals;
22
23int main(int argc, char** argv) {
24 enum Backends { AST, Dot, H, PY, Md, Mim, Nest, SExpr, SlottedSExpr, ProfileTrace, Num_Backends };
25 // test
26
27 fe::term::resolve_mode(); // colors in std::format-ed output depend on Auto being resolved up front
28
29 try {
30 Driver driver;
31 bool show_help = false;
32 bool show_version = false;
33 bool list_search_paths = false;
34 bool sexpr_include_types = false;
35 DotConfig dot;
36 std::string input, prefix;
37 std::string clang = sys::find_cmd("clang");
38 std::vector<std::string> plugins, search_paths, plugin_args;
39#ifdef MIM_ENABLE_CHECKS
40 std::vector<uint32_t> breakpoints;
41 std::vector<uint32_t> watchpoints;
42#endif
43 std::array<std::string, Num_Backends> output;
44 int verbose = 0;
45 auto inc_verbose = [&](bool) { ++verbose; };
46 auto& flags = driver.flags();
47
48 auto profile = [&](const std::string& t) {
49 if (t == "tree")
50 flags.profile = Flags::Profile::Tree;
51 else if (t == "trace")
52 flags.profile = Flags::Profile::Trace;
53 else
54 flags.profile = Flags::Profile::Summary;
55 if (output[ProfileTrace].empty()) output[ProfileTrace] = "-";
56 };
57 auto profile_path = [&](const std::string& t) {
58 if (t == "-" && flags.profile == mim::Flags::Profile::None)
59 flags.profile = Flags::Profile::Summary;
60 else
61 flags.profile = Flags::Profile::Trace;
62 output[ProfileTrace] = t;
63 };
64
65 // clang-format off
66 auto cli = lyra::cli()
67 | lyra::help(show_help)
68 | lyra::opt(show_version )["-v"]["--version" ]("Display version info and exit.")
69 | lyra::opt(list_search_paths )["-l"]["--list-search-paths" ]("List search paths in order and exit.")
70 | lyra::opt(clang, "clang" )["-c"]["--clang" ]("Path to clang executable (default: '" MIM_WHICH " clang').")
71 | lyra::opt(plugins, "plugin" )["-p"]["--plugin" ]("Dynamically load plugin.")
72 | lyra::opt(search_paths, "path" )["-P"]["--plugin-path" ]("Path to search for plugins.")
73 | lyra::opt(plugin_args, "plugin:arg" )["-X"]["--plugin-arg" ]("Pass <arg> to plugin/phase <plugin>, e.g. -X ll:--target=sm_80. Repeatable.")
74 | lyra::opt(inc_verbose )["-V"]["--verbose" ]("Verbose mode. Multiple -V options increase the verbosity. The maximum is 4.").cardinality(0, 5)
75 | lyra::opt(output[AST], "file" ) ["--output-ast" ]("Directly emits AST representation of input.")
76 | lyra::opt(output[Dot], "file" ) ["--output-dot" ]("Emits the Mim program as a MimIR graph using Graphviz' DOT language.")
77 | lyra::opt(output[H ], "file" ) ["--output-h" ]("Emits a header file to be used to interface with a plugin in C++.")
78 | lyra::opt(output[PY ], "file" ) ["--output-py" ]("Emits a Python enum to be used to interface with a plugin in Python.")
79 | lyra::opt(output[Md ], "file" ) ["--output-md" ]("Emits the input formatted as Markdown.")
80 | lyra::opt(output[Mim], "file" )["-o"]["--output-mim" ]("Emits the Mim program again.")
81 | lyra::opt(output[Nest], "file" ) ["--output-nest" ]("Emits program nesting tree as Dot.")
82 | lyra::opt(output[SExpr],"file" ) ["--output-sexpr" ]("Emits the program as symbolic expression.")
83 | lyra::opt(output[SlottedSExpr],"file" ) ["--output-sexpr-slotted" ]("Emits the program as symbolic expression that follows the format required by slotted-egraphs.")
84 | lyra::opt(flags.force_load ) ["--force-load" ]("Load plugins even on version mismatch.")
85 | lyra::opt(profile, "|summary|tree|trace" ) ["--profile" ]("Measure how long each phase takes and write a summary, tree or chrome://tracing compatible output to the output-profile provided destination.")
86 | lyra::opt(profile_path, "file" ) ["--output-profile" ]("The output path (or '-' for stdout) for the profiling information.")
87 | lyra::opt(flags.ascii )["-a"]["--ascii" ]("Use ASCII alternatives in output instead of UTF-8.")
88 | lyra::opt(flags.bootstrap ) ["--bootstrap" ]("Puts mim into \"bootstrap mode\". This means a 'plugin' directive has the same effect as an 'import' and will not load a library. In addition, no standard plugins will be loaded.")
89 | lyra::opt(sexpr_include_types ) ["--sexpr-include-types" ]("Wraps symbolic expression terms in a type annotation. Types will not be wrapped in type annotations.")
90 | lyra::opt(dot.follow_types ) ["--dot-follow-types" ]("Follow type dependencies in DOT output.")
91 | lyra::opt(dot.all_annexes ) ["--dot-all-annexes" ]("Output all annexes - even if unused - in DOT output.")
92 | lyra::opt(dot.inline_consts ) ["--dot-inline-consts" ]("Wire up literals, axioms, etc. with normal edges in DOT output instead of detaching them into a separate row; useful for small graphs.")
93 | lyra::opt(dot.default_filter ) ["--dot-default-filter" ]("Always show a lambda's filter in DOT output - even if it is the default one (ff for continuations, tt for direct-style functions).")
94 | lyra::opt(dot.show_hidden ) ["--dot-show-hidden" ]("Render otherwise-transparent detached edges in DOT output (Var->binder back-edges, shared literals/axioms, and type edges) in a subtle gray instead of fully transparent.")
95 | lyra::opt(flags.dump_recursive ) ["--dump-recursive" ]("Dumps Mim program with a simple recursive algorithm that is not readable again from Mim but is less fragile and also works for broken Mim programs.")
96 | lyra::opt(flags.aggressive_lam_spec ) ["--aggr-lam-spec" ]("Overrides LamSpec behavior to follow recursive calls.")
97 | lyra::opt(flags.scalarize_threshold, "threshold") ["--scalarize-threshold" ]("MimIR will not scalarize tuples/packs/sigmas/arrays with a number of elements greater than or equal this threshold.")
98 | lyra::opt(flags.max_fp_iters, "num" ) ["--max-fp-iters" ]("Maximum number of fixed-point iterations before a phase errors out; guards against non-monotone analyses.")
100 | lyra::opt(breakpoints, "gid" )["-b"]["--break" ]("*Triggers breakpoint when creating a node whose global id is <gid>.")
101 | lyra::opt(watchpoints, "gid" )["-w"]["--watch" ]("*Triggers breakpoint when setting a node whose global id is <gid>.")
102 | lyra::opt(flags.reeval_breakpoints ) ["--reeval-breakpoints" ]("*Triggers breakpoint even upon unfying a node that has already been built.")
103 | lyra::opt(flags.break_on_alpha ) ["--break-on-alpha" ]("*Triggers breakpoint as soon as two expressions turn out to be not alpha-equivalent.")
104 | lyra::opt(flags.break_on_error ) ["--break-on-error" ]("*Triggers breakpoint on ELOG.")
105 | lyra::opt(flags.break_on_warn ) ["--break-on-warn" ]("*Triggers breakpoint on WLOG.")
106 | lyra::opt(flags.trace_gids ) ["--trace-gids" ]("*Output gids during World::unify/insert.")
107#endif
108 | lyra::arg(input, "file" ) ("Input file.")
109 ;
110 // clang-format on
111
112 if (auto result = cli.parse({argc, argv}); !result) throw std::invalid_argument(result.message());
113
114 if (show_help) {
115 std::cout << cli << std::endl;
116#ifdef MIM_ENABLE_CHECKS
117 std::cout << "*These are developer options only enabled, if 'MIM_ENABLE_CHECKS' is ON." << std::endl;
118#endif
119 std::cout << "Use \"-\" as <file> to output to stdout." << std::endl;
120 return EXIT_SUCCESS;
121 }
122
123 if (show_version) {
124 std::cout << "mim " << driver.version() << std::endl;
125 std::exit(EXIT_SUCCESS);
126 }
127
128 for (auto&& path : search_paths)
129 driver.add_search_path(path);
130
131 for (auto&& pa : plugin_args) {
132 auto pos = pa.find(':');
133 if (pos == std::string::npos)
134 throw std::invalid_argument("error: --plugin-arg expects <plugin>:<arg>, got '" + pa + "'");
135 driver.add_arg(driver.sym(pa.substr(0, pos)), pa.substr(pos + 1));
136 }
137
138 if (list_search_paths) {
139 for (auto&& path : driver.search_paths() | std::views::drop(1)) // skip first empty path
140 std::cout << path << std::endl;
141 std::exit(EXIT_SUCCESS);
142 }
143
144 World& world = driver.world();
145#ifdef MIM_ENABLE_CHECKS
146 for (auto b : breakpoints)
147 world.breakpoint(b);
148 for (auto w : watchpoints)
149 world.watchpoint(w);
150#endif
151 driver.log().set(&std::cerr).set((Log::Level)verbose);
152
153 // prepare output files and streams
154 std::array<std::ofstream, Num_Backends> ofs;
155 std::array<std::ostream*, Num_Backends> os;
156 os.fill(nullptr);
157 for (size_t be = 0; be != Num_Backends; ++be) {
158 if (output[be].empty()) continue;
159 if (output[be] == "-") {
160 os[be] = &std::cout;
161 } else {
162 ofs[be].open(output[be]);
163 os[be] = &ofs[be];
164 }
165 }
166
167 if (input.empty()) throw std::invalid_argument("error: no input given");
168 if (input[0] == '-' || input.substr(0, 2) == "--")
169 throw std::invalid_argument("error: unknown option " + input);
170
171 try {
172 auto path = fs::path(input);
173 world.set(path.filename().replace_extension().string());
174
175 auto ast = ast::AST(world);
176 auto parser = ast::Parser(ast);
177
178 if (auto mod = parser.import_main(input, plugins, os[Md])) {
179 if (auto s = os[AST]) {
180 auto tab = fe::Tab::spaces();
181 mod->stream(tab, *s);
182 }
183
184 auto h = os[H];
185 auto py = os[PY];
186 if (h || py) {
187 mod->bind(ast);
188 ast.error().ack();
189 auto plugin = world.sym(fs::path{path}.filename().replace_extension().string());
190 if (h) ast.bootstrap(plugin, *h);
191 if (py) ast.bootstrap_py(plugin, *py);
192 return EXIT_SUCCESS;
193 }
194
195 mod->compile(ast);
196 optimize(world);
197
198 if (auto s = os[Dot]) world.dot(*s, dot);
199 if (auto s = os[Mim]) world.dump(*s);
200 if (auto s = os[Nest]) mim::Nest(world).dot(*s);
201
202 if (auto s = os[SExpr]) {
203 if (sexpr_include_types)
204 sexpr::emit_typed(world, *s);
205 else
206 sexpr::emit(world, *s);
207 }
208 if (auto s = os[SlottedSExpr]) {
209 if (sexpr_include_types)
210 sexpr::emit_slotted_typed(world, *s);
211 else
212 sexpr::emit_slotted(world, *s);
213 }
214 if (auto s = os[ProfileTrace]) {
215 switch (flags.profile) {
216 case Flags::Profile::Summary: driver.profiler().summary(*s); break;
217 case Flags::Profile::Tree: driver.profiler().tree(*s); break;
218 case Flags::Profile::Trace: driver.profiler().chrome_trace(*s); break;
219 case Flags::Profile::None: break;
220 }
221 }
222 } else {
223 fe::throwf("couldn't read file '{}'", input);
224 }
225 } catch (const Error& e) { // e.loc.path doesn't exist anymore in outer scope so catch Error here
226 std::cerr << e;
227 return EXIT_FAILURE;
228 }
229 } catch (const std::exception& e) {
230 std::println(std::cerr, "{}", e.what());
231 return EXIT_FAILURE;
232 } catch (...) {
233 std::println(std::cerr, "error: unknown exception");
234 return EXIT_FAILURE;
235 }
236
237 return EXIT_SUCCESS;
238}
Some "global" variables needed all over the place.
Definition driver.h:20
void add_search_path(fs::path path)
Definition driver.h:53
Profiler & profiler()
Definition driver.h:38
const Version & version() const
MimIR Version.
Definition driver.h:41
World & world()
Definition driver.h:40
Log & log() const
Definition driver.h:37
Flags & flags()
Definition driver.h:35
void add_arg(Sym plugin, std::string arg)
Definition driver.h:133
const auto & search_paths() const
Definition driver.h:52
Log & set(std::ostream *ostream)
Definition log.h:38
Level
Definition log.h:23
Builds a nesting tree for all mutables/binders.
Definition nest.h:30
void dot(std::ostream &os) const
Definition dot.cpp:246
void chrome_trace(std::ostream &) const
Dumps all Spans as Chrome Trace Event Format JSON.
Definition profile.cpp:116
void summary(std::ostream &) const
Definition profile.cpp:47
void tree(std::ostream &) const
Prints the Spans as an indented tree, preserving the order in which Phasees ran.
Definition profile.cpp:94
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:36
void watchpoint(u32 gid)
Trigger breakpoint in your debugger when Def::setting a Def with this gid.
Definition world.cpp:747
void dot(std::ostream &os, DotConfig cfg={}) const
Dumps DOT to os, configured via cfg (see DotConfig).
Definition dot.cpp:222
void set(Sym name)
Definition world.h:98
Sym sym(std::string_view)
Definition world.cpp:105
void breakpoint(u32 gid)
Trigger breakpoint in your debugger when creating a Def with this gid.
Definition world.cpp:746
void dump(std::ostream &os)
Dump to os.
Definition dump.cpp:566
Parses Mim code as AST.
Definition parser.h:30
#define MIM_ENABLE_CHECKS
Definition config.h:3
int main(int argc, char **argv)
Definition main.cpp:23
Definition Mim.cmake:1
Definition ast.h:14
void emit_slotted(World &, std::ostream &)
Definition sexpr.cpp:956
void emit_typed(World &, std::ostream &)
Definition sexpr.cpp:951
void emit(World &, std::ostream &)
Definition sexpr.cpp:946
void emit_slotted_typed(World &, std::ostream &)
Definition sexpr.cpp:961
std::string find_cmd(std::string)
Definition sys.cpp:95
Definition ast.h:14
bool follow_types
Follow Def::type() dependencies.
Definition def.h:220
bool inline_consts
Wire up literals, axioms, etc. with normal edges instead of detaching them.
Definition def.h:221
void optimize(World &)
Runs _compile or _default_compile, if available (in this order).
Definition optimize.cpp:8
bool all_annexes
Include all annexes - even if unused (World::dot only).
Definition def.h:219
bool show_hidden
Render otherwise-transparent detached edges (Var→binder back-edges, shared literals/axioms,...
Definition def.h:223
bool default_filter
Show Lam::filter() even if it has its default value.
Definition def.h:222
Options for Def::dot and World::dot.
Definition def.h:217
@ Summary
Flat table aggregated by Phase name.
Definition flags.h:15
@ Tree
Indented tree preserving the order in which Phasees ran.
Definition flags.h:16
@ None
No profiling.
Definition flags.h:14
@ Trace
chrome://tracing compatible output.
Definition flags.h:17
#define MIM_WHICH
Definition sys.h:12