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