1
0
mirror of https://github.com/exaloop/codon.git synced 2025-06-03 15:03:52 +08:00
codon/runtime/main.cpp
A. R. Shajii 50bd57fed6 Rename
2021-09-30 15:55:17 -04:00

298 lines
9.8 KiB
C++

#include "dsl/plugins.h"
#include "parser/parser.h"
#include "sir/llvm/llvisitor.h"
#include "sir/transform/manager.h"
#include "sir/transform/pass.h"
#include "util/common.h"
#include "llvm/Support/CommandLine.h"
#include <algorithm>
#include <chrono>
#include <cstdlib>
#include <string>
#include <unordered_map>
#include <vector>
namespace {
void versMsg(llvm::raw_ostream &out) {
out << CODON_VERSION_MAJOR << "." << CODON_VERSION_MINOR << "." << CODON_VERSION_PATCH
<< "\n";
}
const std::vector<std::string> &supportedExtensions() {
static const std::vector<std::string> extensions = {".seq", ".py"};
return extensions;
}
bool hasExtension(const std::string &filename, const std::string &extension) {
return filename.size() >= extension.size() &&
filename.compare(filename.size() - extension.size(), extension.size(),
extension) == 0;
}
std::string trimExtension(const std::string &filename, const std::string &extension) {
if (hasExtension(filename, extension)) {
return filename.substr(0, filename.size() - extension.size());
} else {
return filename;
}
}
std::string makeOutputFilename(const std::string &filename,
const std::string &extension) {
for (const auto &ext : supportedExtensions()) {
if (hasExtension(filename, ext))
return trimExtension(filename, ext) + extension;
}
return filename + extension;
}
enum BuildKind { LLVM, Bitcode, Object, Executable, Detect };
enum OptMode { Debug, Release };
struct ProcessResult {
std::unique_ptr<seq::ir::LLVMVisitor> visitor;
std::string input;
};
} // namespace
int docMode(const std::vector<const char *> &args, const std::string &argv0) {
llvm::cl::ParseCommandLineOptions(args.size(), args.data());
seq::generateDocstr(argv0);
return EXIT_SUCCESS;
}
ProcessResult processSource(const std::vector<const char *> &args) {
llvm::cl::opt<std::string> input(llvm::cl::Positional, llvm::cl::desc("<input file>"),
llvm::cl::init("-"));
llvm::cl::opt<OptMode> optMode(
llvm::cl::desc("optimization mode"),
llvm::cl::values(
clEnumValN(Debug, "debug",
"Turn off compiler optimizations and show backtraces"),
clEnumValN(Release, "release",
"Turn on compiler optimizations and disable debug info")),
llvm::cl::init(Debug));
llvm::cl::list<std::string> defines(
"D", llvm::cl::Prefix,
llvm::cl::desc("Add static variable definitions. The syntax is <name>=<value>"));
llvm::cl::list<std::string> disabledOpts(
"disable-opt", llvm::cl::desc("Disable the specified IR optimization"));
llvm::cl::list<std::string> dsls("dsl", llvm::cl::desc("Use specified DSL"));
llvm::cl::ParseCommandLineOptions(args.size(), args.data());
auto &exts = supportedExtensions();
if (input != "-" && std::find_if(exts.begin(), exts.end(), [&](auto &ext) {
return hasExtension(input, ext);
}) == exts.end())
seq::compilationError(
"input file is expected to be a .seq/.py file, or '-' for stdin");
std::unordered_map<std::string, std::string> defmap;
for (const auto &define : defines) {
auto eq = define.find('=');
if (eq == std::string::npos || !eq) {
seq::compilationWarning("ignoring malformed definition: " + define);
continue;
}
auto name = define.substr(0, eq);
auto value = define.substr(eq + 1);
if (defmap.find(name) != defmap.end()) {
seq::compilationWarning("ignoring duplicate definition: " + define);
continue;
}
defmap.emplace(name, value);
}
auto *module = seq::parse(args[0], input.c_str(), /*code=*/"", /*isCode=*/false,
/*isTest=*/false, /*startLine=*/0, defmap);
if (!module)
return {{}, {}};
const bool isDebug = (optMode == OptMode::Debug);
auto t = std::chrono::high_resolution_clock::now();
std::vector<std::string> disabledOptsVec(disabledOpts);
seq::ir::transform::PassManager pm(isDebug, disabledOptsVec);
seq::PluginManager plm(&pm, isDebug);
LOG_TIME("[T] ir-setup = {:.1f}",
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - t)
.count() /
1000.0);
// load other plugins
for (const auto &dsl : dsls) {
auto result = plm.load(dsl);
switch (result) {
case seq::PluginManager::NONE:
break;
case seq::PluginManager::NOT_FOUND:
seq::compilationError("DSL '" + dsl + "' not found");
break;
case seq::PluginManager::NO_ENTRYPOINT:
seq::compilationError("DSL '" + dsl + "' has no entry point");
break;
case seq::PluginManager::UNSUPPORTED_VERSION:
seq::compilationError("DSL '" + dsl + "' version incompatible");
break;
default:
break;
}
}
t = std::chrono::high_resolution_clock::now();
pm.run(module);
LOG_TIME("[T] ir-opt = {:.1f}", std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - t)
.count() /
1000.0);
t = std::chrono::high_resolution_clock::now();
auto visitor = std::make_unique<seq::ir::LLVMVisitor>(isDebug);
visitor->visit(module);
LOG_TIME("[T] ir-visitor = {:.1f}",
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - t)
.count() /
1000.0);
if (_dbg_level)
visitor->dump();
return {std::move(visitor), input};
}
int runMode(const std::vector<const char *> &args) {
llvm::cl::list<std::string> libs(
"l", llvm::cl::desc("Load and link the specified library"));
llvm::cl::list<std::string> seqArgs(llvm::cl::ConsumeAfter,
llvm::cl::desc("<program arguments>..."));
auto start_t = std::chrono::high_resolution_clock::now();
auto result = processSource(args);
if (!result.visitor)
return EXIT_FAILURE;
std::vector<std::string> libsVec(libs);
std::vector<std::string> argsVec(seqArgs);
argsVec.insert(argsVec.begin(), result.input);
LOG_USER("compiler took: {:.2f} seconds",
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start_t)
.count() /
1000.0);
result.visitor->run(argsVec, libsVec);
return EXIT_SUCCESS;
}
int buildMode(const std::vector<const char *> &args) {
llvm::cl::list<std::string> libs(
"l", llvm::cl::desc("Link the specified library (only for executables)"));
llvm::cl::opt<BuildKind> buildKind(
llvm::cl::desc("output type"),
llvm::cl::values(clEnumValN(LLVM, "llvm", "Generate LLVM IR"),
clEnumValN(Bitcode, "bc", "Generate LLVM bitcode"),
clEnumValN(Object, "obj", "Generate native object file"),
clEnumValN(Executable, "exe", "Generate executable"),
clEnumValN(Detect, "detect",
"Detect output type based on output file extension")),
llvm::cl::init(Detect));
llvm::cl::opt<std::string> output(
"o",
llvm::cl::desc(
"Write compiled output to specified file. Supported extensions: "
"none (executable), .o (object file), .ll (LLVM IR), .bc (LLVM bitcode)"));
auto result = processSource(args);
if (!result.visitor)
return EXIT_FAILURE;
std::vector<std::string> libsVec(libs);
if (output.empty() && result.input == "-")
seq::compilationError("output file must be specified when reading from stdin");
std::string extension;
switch (buildKind) {
case BuildKind::LLVM:
extension = ".ll";
break;
case BuildKind::Bitcode:
extension = ".bc";
break;
case BuildKind::Object:
extension = ".o";
break;
case BuildKind::Executable:
case BuildKind::Detect:
extension = "";
break;
default:
assert(0);
}
const std::string filename =
output.empty() ? makeOutputFilename(result.input, extension) : output;
switch (buildKind) {
case BuildKind::LLVM:
result.visitor->writeToLLFile(filename);
break;
case BuildKind::Bitcode:
result.visitor->writeToBitcodeFile(filename);
break;
case BuildKind::Object:
result.visitor->writeToObjectFile(filename);
break;
case BuildKind::Executable:
result.visitor->writeToExecutable(filename, libsVec);
break;
case BuildKind::Detect:
result.visitor->compile(filename, libsVec);
break;
default:
assert(0);
}
return EXIT_SUCCESS;
}
void showCommandsAndExit() {
seq::compilationError("Available commands: seqc <run|build|doc>");
}
int otherMode(const std::vector<const char *> &args) {
llvm::cl::opt<std::string> input(llvm::cl::Positional, llvm::cl::desc("<mode>"));
llvm::cl::extrahelp("\nMODES:\n\n"
" run - run a program interactively\n"
" build - build a program\n"
" doc - generate program documentation\n");
llvm::cl::ParseCommandLineOptions(args.size(), args.data());
if (!input.empty())
showCommandsAndExit();
return EXIT_SUCCESS;
}
int main(int argc, const char **argv) {
if (argc < 2)
showCommandsAndExit();
llvm::cl::SetVersionPrinter(versMsg);
std::vector<const char *> args{argv[0]};
for (int i = 2; i < argc; i++)
args.push_back(argv[i]);
std::string mode(argv[1]);
std::string argv0 = std::string(args[0]) + " " + mode;
if (mode == "run") {
args[0] = argv0.data();
return runMode(args);
}
if (mode == "build") {
args[0] = argv0.data();
return buildMode(args);
}
if (mode == "doc") {
const char *oldArgv0 = args[0];
args[0] = argv0.data();
return docMode(args, oldArgv0);
}
return otherMode({argv, argv + argc});
}