Thorin 1.9.0
The Higher ORder 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 <iostream>
6#include <stdexcept>
7
8#include <lyra/lyra.hpp>
9
10#include "thorin/config.h"
11#include "thorin/driver.h"
12
14#include "thorin/fe/parser.h"
16#include "thorin/pass/pass.h"
18#include "thorin/phase/phase.h"
19#include "thorin/util/sys.h"
20
21using namespace thorin;
22using namespace std::literals;
23
24int main(int argc, char** argv) {
25 enum Backends { D, Dot, H, LL, Md, Thorin, Num_Backends };
26
27 try {
28 static const auto version = "thorin command-line utility version " THORIN_VER "\n";
29
30 Driver driver;
31 bool show_help = false;
32 bool show_version = false;
33 bool list_search_paths = false;
34 bool dot_follow_types = false;
35 bool dot_all_annexes = false;
36 std::string input, prefix;
37 std::string clang = sys::find_cmd("clang");
38 std::vector<std::string> plugins, search_paths;
39#ifdef THORIN_ENABLE_CHECKS
40 std::vector<size_t> breakpoints;
41#endif
42 std::array<std::string, Num_Backends> output;
43 int verbose = 0;
44 int opt = 2;
45 auto inc_verbose = [&](bool) { ++verbose; };
46 auto& flags = driver.flags();
47
48 // clang-format off
49 auto cli = lyra::cli()
50 | lyra::help(show_help)
51 | lyra::opt(show_version )["-v"]["--version" ]("Display version info and exit.")
52 | lyra::opt(list_search_paths )["-l"]["--list-search-paths" ]("List search paths in order and exit.")
53 | lyra::opt(clang, "clang" )["-c"]["--clang" ]("Path to clang executable (default: '" THORIN_WHICH " clang').")
54 | lyra::opt(plugins, "plugin" )["-p"]["--plugin" ]("Dynamically load plugin.")
55 | lyra::opt(search_paths, "path" )["-P"]["--plugin-path" ]("Path to search for plugins.")
56 | lyra::opt(inc_verbose )["-V"]["--verbose" ]("Verbose mode. Multiple -V options increase the verbosity. The maximum is 4.").cardinality(0, 4)
57 | lyra::opt(opt, "level" )["-O"]["--optimize" ]("Optimization level (default: 2).")
58 | 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).")
59 | lyra::opt(output[Dot ], "file" ) ["--output-dot" ]("Emits the Thorin program as a graph using Graphviz' DOT language.")
60 | lyra::opt(output[H ], "file" ) ["--output-h" ]("Emits a header file to be used to interface with a plugin in C++.")
61 | lyra::opt(output[LL ], "file" ) ["--output-ll" ]("Compiles the Thorin program to LLVM.")
62 | lyra::opt(output[Md ], "file" ) ["--output-md" ]("Emits the input formatted as Markdown.")
63 | lyra::opt(output[Thorin], "file" )["-o"]["--output-thorin" ]("Emits the Thorin program again.")
64 | lyra::opt(flags.ascii )["-a"]["--ascii" ]("Use ASCII alternatives in output instead of UTF-8.")
65 | lyra::opt(flags.bootstrap ) ["--bootstrap" ]("Puts thorin 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.")
66 | lyra::opt(dot_follow_types ) ["--dot-follow-types" ]("Follow type dependencies in DOT output.")
67 | lyra::opt(dot_all_annexes ) ["--dot-all-annexes" ]("Output all annexes - even if unused - in DOT output.")
68 | 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.")
69 | 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.")
70 | lyra::opt(flags.aggressive_lam_spec ) ["--aggr-lam-spec" ]("Overrides LamSpec behavior to follow recursive calls.")
71 | lyra::opt(flags.scalerize_threshold, "threshold") ["--scalerize-threshold" ]("Thorin will not scalerize tuples/packs/sigmas/arrays with a number of elements greater than or equal this threshold.")
73 | lyra::opt(breakpoints, "gid" )["-b"]["--break" ]("*Triggers breakpoint upon construction of node with global id <gid>. Useful when running in a debugger.")
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_unequal ) ["--break-on-alpha-unequal"]("*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 THORIN_ENABLE_CHECKS
89 std::cout << "*These are developer options only enabled, if 'THORIN_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) driver.add_search_path(path);
101
102 if (list_search_paths) {
103 for (auto&& path : driver.search_paths() | std::views::drop(1)) // skip first empty path
104 std::cout << path << std::endl;
105 std::exit(EXIT_SUCCESS);
106 }
107
108 World& world = driver.world();
109#ifdef THORIN_ENABLE_CHECKS
110 for (auto b : breakpoints) world.breakpoint(b);
111#endif
112 driver.log().set(&std::cerr).set((Log::Level)verbose);
113
114 // prepare output files and streams
115 std::array<std::ofstream, Num_Backends> ofs;
116 std::array<std::ostream*, Num_Backends> os;
117 os.fill(nullptr);
118 for (size_t be = 0; be != Num_Backends; ++be) {
119 if (output[be].empty()) continue;
120 if (output[be] == "-") {
121 os[be] = &std::cout;
122 } else {
123 ofs[be].open(output[be]);
124 os[be] = &ofs[be];
125 }
126 }
127
128 // we always need standard plugins, as long as we are not in bootstrap mode
129 if (!flags.bootstrap) plugins.insert(plugins.end(), {"compile", "opt"});
130
131 if (!plugins.empty())
132 for (const auto& plugin : plugins) driver.load(plugin);
133
134 if (input.empty()) throw std::invalid_argument("error: no input given");
135 if (input[0] == '-' || input.substr(0, 2) == "--")
136 throw std::invalid_argument("error: unknown option " + input);
137
138 auto path = fs::path(input);
139 world.set(path.filename().replace_extension().string());
140 auto parser = Parser(world);
141 parser.import(driver.sym(input), os[Md]);
142
143 if (auto dep = os[D]) {
144 if (auto autogen_h = output[H]; !autogen_h.empty()) {
145 *dep << autogen_h << ": ";
146 assert(!driver.imports().empty());
147 for (auto sep = ""; const auto& [path, _] : driver.imports() | std::views::drop(1)) {
148 *dep << sep << path;
149 sep = " \\\n ";
150 }
151 } else {
152 throw std::invalid_argument("error: --output-d requires --output-h");
153 }
154 *dep << std::endl;
155 }
156
157 if (flags.bootstrap) {
158 if (auto h = os[H])
159 bootstrap(driver, world.sym(fs::path{path}.filename().replace_extension().string()), *h);
160 opt = std::min(opt, 1);
161 }
162
163 switch (opt) {
164 case 0: break;
165 case 1: Phase::run<Cleanup>(world); break;
166 case 2:
167 parser.import("opt");
168 optimize(world);
169 break;
170 default: error("illegal optimization level '{}'", opt);
171 }
172
173 if (os[Thorin]) world.dump(*os[Thorin]);
174 if (os[Dot]) world.dot(*os[Dot], dot_all_annexes, dot_follow_types);
175
176 if (os[LL]) {
177 if (auto backend = driver.backend("ll"))
178 backend(world, *os[LL]);
179 else
180 error("'ll' emitter not loaded; try loading 'mem' plugin");
181 }
182 } catch (const std::exception& e) {
183 errln("{}", e.what());
184 return EXIT_FAILURE;
185 } catch (...) {
186 errln("error: unknown exception");
187 return EXIT_FAILURE;
188 }
189
190 return EXIT_SUCCESS;
191}
Some "global" variables needed all over the place.
Definition driver.h:17
Flags & flags()
Definition driver.h:23
Parses Thorin code into the provided World.
Definition parser.h:33
The World represents the whole program and manages creation of Thorin nodes (Defs).
Definition world.h:35
void set(Sym name)
Definition world.h:87
Sym sym(std::string_view)
Definition world.cpp:77
void dot(std::ostream &os, bool annexes=false, bool types=false) const
Definition dot.cpp:144
void dump(std::ostream &os)
Dump to os.
Definition dump.cpp:436
void breakpoint(u32 gid)
Trigger breakpoint in your debugger when creating Def with Def::gid gid.
Definition world.cpp:543
#define THORIN_VER
Definition config.h:5
#define THORIN_ENABLE_CHECKS
Definition config.h:3
int main(int argc, char **argv)
Definition main.cpp:24
std::string find_cmd(std::string)
Definition sys.cpp:64
Definition cfg.h:11
std::ostream & errln(const char *fmt, Args &&... args)
Definition print.h:179
void optimize(World &)
Definition optimize.cpp:19
void error(const Def *def, const char *fmt, Args &&... args)
Definition def.h:622
absl::btree_map< std::string, void(*)(World &, std::ostream &)> Backends
Definition plugin.h:23
void bootstrap(Driver &, Sym, std::ostream &)
Definition bootstrap.cpp:11
#define THORIN_WHICH
Definition sys.h:10