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
13#include "mim/ast/parser.h"
14#include "mim/pass/optimize.h"
15#include "mim/phase/phase.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, D, 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<size_t> breakpoints;
38#endif
39 std::array<std::string, Num_Backends> output;
40 int verbose = 0;
41 int opt = 2;
42 auto inc_verbose = [&](bool) { ++verbose; };
43 auto& flags = driver.flags();
44
45 // clang-format off
46 auto cli = lyra::cli()
47 | lyra::help(show_help)
48 | lyra::opt(show_version )["-v"]["--version" ]("Display version info and exit.")
49 | lyra::opt(list_search_paths )["-l"]["--list-search-paths" ]("List search paths in order and exit.")
50 | lyra::opt(clang, "clang" )["-c"]["--clang" ]("Path to clang executable (default: '" MIM_WHICH " clang').")
51 | lyra::opt(plugins, "plugin" )["-p"]["--plugin" ]("Dynamically load plugin.")
52 | lyra::opt(search_paths, "path" )["-P"]["--plugin-path" ]("Path to search for plugins.")
53 | lyra::opt(inc_verbose )["-V"]["--verbose" ]("Verbose mode. Multiple -V options increase the verbosity. The maximum is 4.").cardinality(0, 4)
54 | lyra::opt(opt, "level" )["-O"]["--optimize" ]("Optimization level (default: 2).")
55 | lyra::opt(output[AST], "file" ) ["--output-ast" ]("Directly emits AST represntation of input.")
56 | lyra::opt(output[D ], "file" ) ["--output-d" ]("Emits dependency file containing a rule suitable for 'make' describing the dependencies of the source file (requires --output-h).")
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 upon construction of node with global id <gid>. Useful when running in a debugger.")
73 | lyra::opt(flags.reeval_breakpoints ) ["--reeval-breakpoints" ]("*Triggers breakpoint even upon unfying a node that has already been built.")
74 | lyra::opt(flags.break_on_alpha ) ["--break-on-alpha" ]("*Triggers breakpoint as soon as two expressions turn out to be not alpha-equivalent.")
75 | lyra::opt(flags.break_on_error ) ["--break-on-error" ]("*Triggers breakpoint on ELOG.")
76 | lyra::opt(flags.break_on_warn ) ["--break-on-warn" ]("*Triggers breakpoint on WLOG.")
77 | lyra::opt(flags.trace_gids ) ["--trace-gids" ]("*Output gids during World::unify/insert.")
78#endif
79 | lyra::arg(input, "file" ) ("Input file.")
80 ;
81 // clang-format on
82
83 if (auto result = cli.parse({argc, argv}); !result) throw std::invalid_argument(result.message());
84
85 if (show_help) {
86 std::cout << cli << std::endl;
87#ifdef MIM_ENABLE_CHECKS
88 std::cout << "*These are developer options only enabled, if 'MIM_ENABLE_CHECKS' is ON." << std::endl;
89#endif
90 std::cout << "Use \"-\" as <file> to output to stdout." << std::endl;
91 return EXIT_SUCCESS;
92 }
93
94 if (show_version) {
95 std::cerr << version;
96 std::exit(EXIT_SUCCESS);
97 }
98
99 for (auto&& path : search_paths) driver.add_search_path(path);
100
101 if (list_search_paths) {
102 for (auto&& path : driver.search_paths() | std::views::drop(1)) // skip first empty path
103 std::cout << path << std::endl;
104 std::exit(EXIT_SUCCESS);
105 }
106
107 World& world = driver.world();
108#ifdef MIM_ENABLE_CHECKS
109 for (auto b : breakpoints) world.breakpoint(b);
110#endif
111 driver.log().set(&std::cerr).set((Log::Level)verbose);
112
113 // prepare output files and streams
114 std::array<std::ofstream, Num_Backends> ofs;
115 std::array<std::ostream*, Num_Backends> os;
116 os.fill(nullptr);
117 for (size_t be = 0; be != Num_Backends; ++be) {
118 if (output[be].empty()) continue;
119 if (output[be] == "-") {
120 os[be] = &std::cout;
121 } else {
122 ofs[be].open(output[be]);
123 os[be] = &ofs[be];
124 }
125 }
126
127 if (input.empty()) throw std::invalid_argument("error: no input given");
128 if (input[0] == '-' || input.substr(0, 2) == "--")
129 throw std::invalid_argument("error: unknown option " + input);
130
131 // we always need standard plugins, as long as we are not in bootstrap mode
132 if (!flags.bootstrap) {
133 plugins.insert(plugins.begin(), "compile"s);
134 if (opt >= 2) plugins.emplace_back("opt"s);
135 }
136
137 try {
138 auto path = fs::path(input);
139 world.set(path.filename().replace_extension().string());
140
141 auto ast = ast::AST(world);
142 auto parser = ast::Parser(ast);
144
145 for (const auto& plugin : plugins) {
146 auto mod = parser.plugin(plugin);
147 auto import
148 = ast.ptr<ast::Import>(Loc(), ast::Tok::Tag::K_plugin, Dbg(driver.sym(plugin)), std::move(mod));
149 imports.emplace_back(std::move(import));
150 }
151 auto mod = parser.import(driver.sym(input), os[Md]);
152 mod->add_implicit_imports(std::move(imports));
153 mod->compile(ast);
154
155 if (auto s = os[AST]) {
156 Tab tab;
157 mod->stream(tab, *s);
158 }
159
160 if (auto dep = os[D]) {
161 if (auto autogen_h = output[H]; !autogen_h.empty()) {
162 *dep << autogen_h << ": ";
163 assert(!driver.import_syms().empty());
164 for (auto sep = ""; auto path : driver.import_paths() | std::views::drop(1)) {
165 *dep << sep << sys::escape(path);
166 sep = " \\\n ";
167 }
168 } else {
169 throw std::invalid_argument("error: --output-d requires --output-h");
170 }
171 *dep << std::endl;
172 }
173
174 if (flags.bootstrap) {
175 if (auto h = os[H]) {
176 auto plugin = world.sym(fs::path{path}.filename().replace_extension().string());
177 ast.bootstrap(plugin, *h);
178 }
179 opt = std::min(opt, 1);
180 }
181
182 switch (opt) {
183 case 0: break;
184 case 1: Phase::run<Cleanup>(world); break;
185 case 2: optimize(world); break;
186 default: error("illegal optimization level '{}'", opt);
187 }
188
189 if (auto s = os[Dot]) world.dot(*s, dot_all_annexes, dot_follow_types);
190 if (auto s = os[Mim]) world.dump(*s);
191 if (auto s = os[Nest]) mim::Nest(world).dot(*s);
192
193 if (auto s = os[LL]) {
194 if (auto backend = driver.backend("ll"))
195 backend(world, *s);
196 else
197 error("'ll' emitter not loaded; try loading 'mem' plugin");
198 }
199 } catch (const Error& e) { // e.loc.path doesn't exist anymore in outer scope so catch Error here
200 std::cerr << e;
201 return EXIT_FAILURE;
202 }
203 } catch (const std::exception& e) {
204 errln("{}", e.what());
205 return EXIT_FAILURE;
206 } catch (...) {
207 errln("error: unknown exception");
208 return EXIT_FAILURE;
209 }
210
211 return EXIT_SUCCESS;
212}
Some "global" variables needed all over the place.
Definition driver.h:17
void add_search_path(fs::path path)
Definition driver.h:37
auto backend(std::string_view name)
Definition driver.h:78
Log & log()
Definition driver.h:24
World & world()
Definition driver.h:25
auto import_paths()
Definition driver.h:48
Flags & flags()
Definition driver.h:23
auto import_syms()
Definition driver.h:49
const auto & search_paths() const
Definition driver.h:36
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:168
virtual void run()
Entry point and generates some debug output; invokes Phase::start.
Definition phase.cpp:5
Keeps track of indentation level.
Definition print.h:195
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:33
void set(Sym name)
Definition world.h:84
Sym sym(std::string_view)
Definition world.cpp:76
void breakpoint(u32 gid)
Trigger breakpoint in your debugger when creating Def with Def::gid gid.
Definition world.cpp:555
void dump(std::ostream &os)
Dump to os.
Definition dump.cpp:466
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
std::deque< Ptr< T > > Ptrs
Definition ast.h:22
std::string escape(const std::filesystem::path &path)
Returns the path as std::string and escapes all whitespaces with backslash.
Definition sys.cpp:86
std::string find_cmd(std::string)
Definition sys.cpp:65
Definition ast.h:14
void optimize(World &)
Definition optimize.cpp:10
void error(Loc loc, const char *f, Args &&... args)
Definition dbg.h:122
absl::btree_map< std::string, void(*)(World &, std::ostream &)> Backends
Definition plugin.h:23
std::ostream & errln(const char *fmt, Args &&... args)
Definition print.h:190
#define MIM_WHICH
Definition sys.h:10