forked from mimir/mimir
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
164 lines (141 loc) · 7.09 KB
/
main.cpp
File metadata and controls
164 lines (141 loc) · 7.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <lyra/lyra.hpp>
#include "thorin/config.h"
#include "thorin/dialects.h"
#include "thorin/be/dot/dot.h"
#include "thorin/fe/parser.h"
#include "thorin/pass/optimize.h"
#include "thorin/pass/pass.h"
#include "thorin/pass/pipelinebuilder.h"
#include "thorin/phase/phase.h"
#include "thorin/util/sys.h"
using namespace thorin;
using namespace std::literals;
enum Backends { Dot, H, LL, Md, Thorin, Num_Backends };
int main(int argc, char** argv) {
try {
static const auto version = "thorin command-line utility version " THORIN_VER "\n";
World::State state;
bool show_help = false;
bool show_version = false;
std::string input, prefix;
std::string clang = sys::find_cmd("clang");
std::vector<std::string> dialect_plugins, dialect_paths;
std::vector<size_t> breakpoints;
std::array<std::string, Num_Backends> output;
int verbose = 0;
int opt = 2;
auto inc_verbose = [&](bool) { ++verbose; };
auto& flags = state.pod.flags;
// clang-format off
auto cli = lyra::cli()
| lyra::help(show_help)
| lyra::opt(show_version )["-v"]["--version" ]("Display version info and exit.")
| lyra::opt(clang, "clang" )["-c"]["--clang" ]("Path to clang executable (default: '" THORIN_WHICH " clang').")
| lyra::opt(dialect_plugins,"dialect")["-d"]["--dialect" ]("Dynamically load dialect [WIP].")
| lyra::opt(dialect_paths, "path" )["-D"]["--dialect-path" ]("Path to search dialects in.")
| lyra::opt(inc_verbose )["-V"]["--verbose" ]("Verbose mode. Multiple -V options increase the verbosity. The maximum is 4.").cardinality(0, 4)
| lyra::opt(opt, "level" )["-O"]["--optimize" ]("Optimization level (default: 2).")
| lyra::opt(output[Dot ], "file" ) ["--output-dot" ]("Emits the Thorin program as a graph using Graphviz' DOT language.")
| lyra::opt(output[H ], "file" ) ["--output-h" ]("Emits a header file to be used to interface with a dialect in C++.")
| lyra::opt(output[LL ], "file" ) ["--output-ll" ]("Compiles the Thorin program to LLVM.")
| lyra::opt(output[Md ], "file" ) ["--output-md" ]("Emits the input formatted as Markdown.")
| lyra::opt(output[Thorin], "file" )["-o"]["--output-thorin" ]("Emits the Thorin program again.")
| 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.")
| lyra::opt(flags.dump_recursive ) ["--dump-recursive" ]("Dumps Thorin program with a simple recursive algorithm that is not readable again from Thorin but is less fragile and also works for broken Thorin programs.")
#if THORIN_ENABLE_CHECKS
| lyra::opt(breakpoints, "gid" )["-b"]["--break" ]("Trigger breakpoint upon construction of node with global id <gid>. Useful when running in a debugger.")
| lyra::opt(flags.reeval_breakpoints ) ["--reeval-breakpoints"]("Triggers breakpoint even upon unfying a node that has already been built.")
| lyra::opt(flags.trace_gids ) ["--trace-gids" ]("Output gids during World::unify/insert.")
#endif
| lyra::arg(input, "file" ) ("Input file.")
;
// clang-format on
if (auto result = cli.parse({argc, argv}); !result) throw std::invalid_argument(result.message());
if (show_help) {
std::cout << cli << std::endl;
std::cout << "Use \"-\" as <file> to output to stdout." << std::endl;
return EXIT_SUCCESS;
}
if (show_version) {
std::cerr << version;
std::exit(EXIT_SUCCESS);
}
#if THORIN_ENABLE_CHECKS
for (auto b : breakpoints) state.breakpoints.emplace(b);
#endif
World world(state);
world.log().ostream = &std::cerr;
world.log().level = (Log::Level)verbose;
// prepare output files and streams
std::array<std::ofstream, Num_Backends> ofs;
std::array<std::ostream*, Num_Backends> os;
os.fill(nullptr);
for (size_t be = 0; be != Num_Backends; ++be) {
if (output[be].empty()) continue;
if (output[be] == "-") {
os[be] = &std::cout;
} else {
ofs[be].open(output[be]);
os[be] = &ofs[be];
}
}
// we always need core and mem, as long as we are not in bootstrap mode..
if (!os[H]) dialect_plugins.insert(dialect_plugins.end(), {"core", "mem", "compile", "opt"});
std::vector<Dialect> dialects;
thorin::Backends backends;
thorin::Normalizers normalizers;
thorin::Passes passes;
if (!dialect_plugins.empty()) {
for (const auto& dialect : dialect_plugins) {
dialects.push_back(Dialect::load(dialect, dialect_paths));
dialects.back().register_backends(backends);
dialects.back().register_normalizers(normalizers);
dialects.back().register_passes(passes);
}
}
if (input.empty()) throw std::invalid_argument("error: no input given");
if (input[0] == '-' || input.substr(0, 2) == "--")
throw std::invalid_argument("error: unknown option " + input);
std::ifstream ifs(input);
if (!ifs) {
errln("error: cannot read file '{}'", input);
return EXIT_FAILURE;
}
for (const auto& dialect : dialects)
fe::Parser::import_module(world, dialect.name(), dialect_paths, &normalizers);
fe::Parser parser(world, input, ifs, dialect_paths, &normalizers, os[Md]);
parser.parse_module();
if (os[H]) {
parser.bootstrap(*os[H]);
opt = std::min(opt, 1);
}
// clang-format off
switch (opt) {
case 0: break;
case 1: Phase::run<Cleanup>(world); break;
case 2: optimize(world, passes, dialects); break;
default: errln("error: illegal optimization level '{}'", opt);
}
// clang-format on
if (os[Thorin]) world.dump(*os[Thorin]);
if (os[Dot]) dot::emit(world, *os[Dot]);
if (os[LL]) {
if (auto it = backends.find("ll"); it != backends.end()) {
it->second(world, *os[LL]);
} else
errln("error: 'll' emitter not loaded. Try loading 'mem' dialect.");
}
} catch (const std::exception& e) {
errln("{}", e.what());
return EXIT_FAILURE;
} catch (...) {
errln("error: unknown exception");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}