MimIR 0.1
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 <lyra/lyra.hpp>
8#include <rang.hpp>
9
10#include "mim/config.h"
11#include "mim/driver.h"
12#include "mim/phase.h"
13
14#include "mim/ast/parser.h"
15#include "mim/pass/optimize.h"
16#include "mim/util/sys.h"
17
18using namespace mim;
19using namespace std::literals;
20
21int main(int argc, char** argv) {
22 enum Backends { AST, Dot, H, LL, Md, Mim, Nest, Num_Backends };
23
24 try {
25 static const auto version = "mim command-line utility version " MIM_VER "\n";
26
27 Driver driver;
28 bool show_help = false;
29 bool show_version = false;
30 bool list_search_paths = false;
31 bool dot_follow_types = false;
32 bool dot_all_annexes = false;
33 std::string input, prefix;
34 std::string clang = sys::find_cmd("clang");
35 std::vector<std::string> plugins, search_paths;
36#ifdef MIM_ENABLE_CHECKS
37 std::vector<uint32_t> breakpoints;
38 std::vector<uint32_t> watchpoints;
39#endif
40 std::array<std::string, Num_Backends> output;
41 int verbose = 0;
42 int opt = 2;
43 auto inc_verbose = [&](bool) { ++verbose; };
44 auto& flags = driver.flags();
45
46 // clang-format off
47 auto cli = lyra::cli()
48 | lyra::help(show_help)
49 | lyra::opt(show_version )["-v"]["--version" ]("Display version info and exit.")
50 | lyra::opt(list_search_paths )["-l"]["--list-search-paths" ]("List search paths in order and exit.")
51 | lyra::opt(clang, "clang" )["-c"]["--clang" ]("Path to clang executable (default: '" MIM_WHICH " clang').")
52 | lyra::opt(plugins, "plugin" )["-p"]["--plugin" ]("Dynamically load plugin.")
53 | lyra::opt(search_paths, "path" )["-P"]["--plugin-path" ]("Path to search for plugins.")
54 | lyra::opt(inc_verbose )["-V"]["--verbose" ]("Verbose mode. Multiple -V options increase the verbosity. The maximum is 4.").cardinality(0, 5)
55 | lyra::opt(opt, "level" )["-O"]["--optimize" ]("Optimization level (default: 2).")
56 | lyra::opt(output[AST], "file" ) ["--output-ast" ]("Directly emits AST represntation of input.")
57 | lyra::opt(output[Dot], "file" ) ["--output-dot" ]("Emits the Mim program as a MimIR graph using Graphviz' DOT language.")
58 | lyra::opt(output[H ], "file" ) ["--output-h" ]("Emits a header file to be used to interface with a plugin in C++.")
59 | lyra::opt(output[LL ], "file" ) ["--output-ll" ]("Compiles the Mim program to LLVM.")
60 | lyra::opt(output[Md ], "file" ) ["--output-md" ]("Emits the input formatted as Markdown.")
61 | lyra::opt(output[Mim], "file" )["-o"]["--output-mim" ]("Emits the Mim program again.")
62 | lyra::opt(output[Nest], "file" ) ["--output-nest" ]("Emits program nesting tree as Dot.")
63 | lyra::opt(flags.ascii )["-a"]["--ascii" ]("Use ASCII alternatives in output instead of UTF-8.")
64 | 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.")
65 | lyra::opt(dot_follow_types ) ["--dot-follow-types" ]("Follow type dependencies in DOT output.")
66 | lyra::opt(dot_all_annexes ) ["--dot-all-annexes" ]("Output all annexes - even if unused - in DOT output.")
67 | lyra::opt(flags.dump_gid, "level" ) ["--dump-gid" ]("Dumps gid of inline expressions as a comment in output if <level> > 0. Use a <level> of 2 to also emit the gid of trivial defs.")
68 | 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.")
69 | lyra::opt(flags.aggressive_lam_spec ) ["--aggr-lam-spec" ]("Overrides LamSpec behavior to follow recursive calls.")
70 | 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.")
72 | lyra::opt(breakpoints, "gid" )["-b"]["--break" ]("*Triggers breakpoint when creating a node whose global id is <gid>.")
73 | lyra::opt(watchpoints, "gid" )["-w"]["--watch" ]("*Triggers breakpoint when setting a node whose global id is <gid>.")
74 | lyra::opt(flags.reeval_breakpoints ) ["--reeval-breakpoints" ]("*Triggers breakpoint even upon unfying a node that has already been built.")
75 | lyra::opt(flags.break_on_alpha ) ["--break-on-alpha" ]("*Triggers breakpoint as soon as two expressions turn out to be not alpha-equivalent.")
76 | lyra::opt(flags.break_on_error ) ["--break-on-error" ]("*Triggers breakpoint on ELOG.")
77 | lyra::opt(flags.break_on_warn ) ["--break-on-warn" ]("*Triggers breakpoint on WLOG.")
78 | lyra::opt(flags.trace_gids ) ["--trace-gids" ]("*Output gids during World::unify/insert.")
79#endif
80 | lyra::arg(input, "file" ) ("Input file.")
81 ;
82 // clang-format on
83
84 if (auto result = cli.parse({argc, argv}); !result) throw std::invalid_argument(result.message());
85
86 if (show_help) {
87 std::cout << cli << std::endl;
88#ifdef MIM_ENABLE_CHECKS
89 std::cout << "*These are developer options only enabled, if 'MIM_ENABLE_CHECKS' is ON." << std::endl;
90#endif
91 std::cout << "Use \"-\" as <file> to output to stdout." << std::endl;
92 return EXIT_SUCCESS;
93 }
94
95 if (show_version) {
96 std::cerr << version;
97 std::exit(EXIT_SUCCESS);
98 }
99
100 for (auto&& path : search_paths)
101 driver.add_search_path(path);
102
103 if (list_search_paths) {
104 for (auto&& path : driver.search_paths() | std::views::drop(1)) // skip first empty path
105 std::cout << path << std::endl;
106 std::exit(EXIT_SUCCESS);
107 }
108
109 World& world = driver.world();
110#ifdef MIM_ENABLE_CHECKS
111 for (auto b : breakpoints)
112 world.breakpoint(b);
113 for (auto w : watchpoints)
114 world.watchpoint(w);
115#endif
116 driver.log().set(&std::cerr).set((Log::Level)verbose);
117
118 // prepare output files and streams
119 std::array<std::ofstream, Num_Backends> ofs;
120 std::array<std::ostream*, Num_Backends> os;
121 os.fill(nullptr);
122 for (size_t be = 0; be != Num_Backends; ++be) {
123 if (output[be].empty()) continue;
124 if (output[be] == "-") {
125 os[be] = &std::cout;
126 } else {
127 ofs[be].open(output[be]);
128 os[be] = &ofs[be];
129 }
130 }
131
132 if (input.empty()) throw std::invalid_argument("error: no input given");
133 if (input[0] == '-' || input.substr(0, 2) == "--")
134 throw std::invalid_argument("error: unknown option " + input);
135
136 try {
137 auto path = fs::path(input);
138 world.set(path.filename().replace_extension().string());
139
140 auto ast = ast::AST(world);
141 auto parser = ast::Parser(ast);
143
144 if (!flags.bootstrap) {
145 plugins.insert(plugins.begin(), "compile"s);
146 if (opt >= 2) plugins.emplace_back("opt"s);
147 }
148
149 for (const auto& plugin : plugins) {
150 auto mod = parser.plugin(plugin);
151 auto import
152 = ast.ptr<ast::Import>(Loc(), ast::Tok::Tag::K_plugin, Dbg(driver.sym(plugin)), std::move(mod));
153 imports.emplace_back(std::move(import));
154 }
155
156 auto mod = parser.import(driver.sym(input), os[Md]);
157 mod->add_implicit_imports(std::move(imports));
158
159 if (auto s = os[AST]) {
160 Tab tab;
161 mod->stream(tab, *s);
162 }
163
164 if (auto h = os[H]) {
165 mod->bind(ast);
166 ast.error().ack();
167 auto plugin = world.sym(fs::path{path}.filename().replace_extension().string());
168 ast.bootstrap(plugin, *h);
169 return EXIT_SUCCESS;
170 }
171
172 mod->compile(ast);
173
174 switch (opt) {
175 case 0: break;
176 case 1: Phase::run<Cleanup>(world); break;
177 case 2: optimize(world); break;
178 default: error("illegal optimization level '{}'", opt);
179 }
180
181 if (auto s = os[Dot]) world.dot(*s, dot_all_annexes, dot_follow_types);
182 if (auto s = os[Mim]) world.dump(*s);
183 if (auto s = os[Nest]) mim::Nest(world).dot(*s);
184
185 if (auto s = os[LL]) {
186 if (auto backend = driver.backend("ll"))
187 backend(world, *s);
188 else
189 error("'ll' emitter not loaded; try loading 'mem' plugin");
190 }
191 } catch (const Error& e) { // e.loc.path doesn't exist anymore in outer scope so catch Error here
192 std::cerr << e;
193 return EXIT_FAILURE;
194 }
195 } catch (const std::exception& e) {
196 errln("{}", e.what());
197 return EXIT_FAILURE;
198 } catch (...) {
199 errln("error: unknown exception");
200 return EXIT_FAILURE;
201 }
202
203 return EXIT_SUCCESS;
204}
Some "global" variables needed all over the place.
Definition driver.h:17
void add_search_path(fs::path path)
Definition driver.h:38
auto backend(std::string_view name)
Definition driver.h:84
World & world()
Definition driver.h:26
Log & log() const
Definition driver.h:25
Flags & flags()
Definition driver.h:23
const auto & search_paths() const
Definition driver.h:37
Log & set(std::ostream *ostream)
Definition log.h:37
Level
Definition log.h:22
Builds a nesting tree of all immutables‍/binders.
Definition nest.h:11
void dot(std::ostream &os) const
Definition dot.cpp:170
virtual void run()
Entry point and generates some debug output; invokes Phase::start.
Definition phase.cpp:13
Keeps track of indentation level.
Definition print.h:206
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:694
void set(Sym name)
Definition world.h:95
Sym sym(std::string_view)
Definition world.cpp:75
void breakpoint(u32 gid)
Trigger breakpoint in your debugger when creating a Def with this gid.
Definition world.cpp:693
void dump(std::ostream &os)
Dump to os.
Definition dump.cpp:469
void dot(std::ostream &os, bool annexes=false, bool types=false) const
Dumps DOT to os.
Definition dot.cpp:146
Parses Mim code as AST.
Definition parser.h:30
#define MIM_ENABLE_CHECKS
Definition config.h:3
#define MIM_VER
Definition config.h:5
int main(int argc, char **argv)
Definition main.cpp:21
Definition ast.h:14
std::deque< Ptr< T > > Ptrs
Definition ast.h:24
std::string find_cmd(std::string)
Definition sys.cpp:65
Definition ast.h:14
void optimize(World &)
Definition optimize.cpp:8
void error(Loc loc, const char *f, Args &&... args)
Definition dbg.h:125
absl::btree_map< std::string, void(*)(World &, std::ostream &)> Backends
Definition plugin.h:24
std::ostream & errln(const char *fmt, Args &&... args)
Definition print.h:201
#define MIM_WHICH
Definition sys.h:10