From 37ac49ab2533b678a3e81d63d3173f20131654c0 Mon Sep 17 00:00:00 2001 From: vla5924 Date: Sun, 7 Apr 2024 13:40:52 +0300 Subject: [PATCH] add timer capabilities to cli, refactor args Co-authored-by: Maksim Shagov <43129418+MaksimShagov@users.noreply.github.com> --- compiler/include/compiler/cli/logger.hpp | 14 +- compiler/include/compiler/utils/timer.hpp | 33 ++++ compiler/lib/cli/compiler.cpp | 188 ++++++++++++++++------ compiler/lib/cli/logger.cpp | 27 ---- 4 files changed, 181 insertions(+), 81 deletions(-) create mode 100644 compiler/include/compiler/utils/timer.hpp diff --git a/compiler/include/compiler/cli/logger.hpp b/compiler/include/compiler/cli/logger.hpp index 23be2217..549a8fd1 100644 --- a/compiler/include/compiler/cli/logger.hpp +++ b/compiler/include/compiler/cli/logger.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include class Logger { @@ -14,11 +15,14 @@ class Logger { void setStdoutEnabled(bool enabled); void setOutputFile(const std::string &filename); - Logger &operator<<(const std::string &); - Logger &operator<<(const char *const); - Logger &operator<<(char); - Logger &operator<<(size_t); - Logger &operator<<(std::ostream &(*)(std::ostream &)); + template + Logger &operator<<(const T &out) { + if (stdoutEnabled) + std::cout << out; + if (fileOutput.is_open()) + fileOutput << out; + return *this; + } private: void closeOutputFile(); diff --git a/compiler/include/compiler/utils/timer.hpp b/compiler/include/compiler/utils/timer.hpp new file mode 100644 index 00000000..7a5186b6 --- /dev/null +++ b/compiler/include/compiler/utils/timer.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace utils { + +struct Timer { + using TimePoint = std::chrono::time_point; + + Timer() = default; + Timer(const Timer &) = default; + Timer(Timer &&) = default; + ~Timer() = default; + + void start() { + startPoint = std::chrono::steady_clock::now(); + } + + void stop() { + stopPoint = std::chrono::steady_clock::now(); + } + + // Obtain elapsed time in milliseconds + auto elapsed() const { + return std::chrono::duration_cast(stopPoint - startPoint).count(); + } + + private: + TimePoint startPoint; + TimePoint stopPoint; +}; + +} // namespace utils diff --git a/compiler/lib/cli/compiler.cpp b/compiler/lib/cli/compiler.cpp index f22277fb..6d881b3d 100644 --- a/compiler/lib/cli/compiler.cpp +++ b/compiler/lib/cli/compiler.cpp @@ -1,7 +1,11 @@ #include "compiler.hpp" +#include #include +#include +#include #include +#include #include #ifdef ENABLE_CODEGEN @@ -19,6 +23,7 @@ #include "compiler/frontend/parser/parser.hpp" #include "compiler/frontend/preprocessor/preprocessor.hpp" #include "compiler/utils/source_files.hpp" +#include "compiler/utils/timer.hpp" #ifdef ENABLE_CODEGEN #include "compiler/codegen/ir_generator.hpp" @@ -33,26 +38,47 @@ using parser::Parser; using preprocessor::Preprocessor; using semantizer::Semantizer; using utils::SourceFile; +using utils::Timer; #ifdef ENABLE_CODEGEN using ir_generator::IRGenerator; #endif -namespace { +namespace arg { -const char *const ARG_HELP = "--help"; -const char *const ARG_VERBOSE = "--verbose"; -const char *const ARG_LOG = "--log"; -const char *const ARG_OPTIMIZE = "--optimize"; -const char *const ARG_FILES = "FILES"; +constexpr std::string_view help = "--help"; +constexpr std::string_view verbose = "--verbose"; +constexpr std::string_view log = "--log"; +constexpr std::string_view optimize = "--optimize"; +constexpr std::string_view time = "--time"; +constexpr std::string_view stopAfter = "--stop-after"; +constexpr std::string_view files = "FILES"; #ifdef ENABLE_CODEGEN -const char *const ARG_COMPILE = "--compile"; -const char *const ARG_CLANG = "--clang"; -const char *const ARG_LLC = "--llc"; -const char *const ARG_OUTPUT = "--output"; +constexpr std::string_view compile = "--compile"; +constexpr std::string_view clang = "--clang"; +constexpr std::string_view llc = "--llc"; +constexpr std::string_view output = "--output"; #endif +} // namespace arg + +namespace stage { + +constexpr std::string_view preprocessor = "preprocessor"; +constexpr std::string_view lexer = "lexer"; +constexpr std::string_view parser = "parser"; +constexpr std::string_view semantizer = "semantizer"; +constexpr std::string_view optimizer = "optimizer"; + +#ifdef ENABLE_CODEGEN +constexpr std::string_view codegen = "codegen"; +#endif + +} // namespace stage + +namespace { + #ifdef ENABLE_CODEGEN std::filesystem::path createTemporaryDirectory() { auto tempDir = std::filesystem::temp_directory_path(); @@ -96,113 +122,173 @@ std::string generateClangCommand(const std::string &clangBin, const std::filesys int Compiler::exec(int argc, char *argv[]) { argparse::ArgumentParser program("compiler", "1.0", argparse::default_arguments::none); - program.add_argument("-h", ARG_HELP).help("show help message and exit").default_value(false).implicit_value(true); - program.add_argument("-v", ARG_VERBOSE).help("print info messages").default_value(false).implicit_value(true); - program.add_argument("-l", ARG_LOG).help("log file (stages output will be saved if provided)"); - program.add_argument("-O", ARG_OPTIMIZE).help("run optimization pass").default_value(false).implicit_value(true); + program.add_argument("-h", arg::help).help("show help message and exit").default_value(false).implicit_value(true); + program.add_argument("-v", arg::verbose).help("print info messages").default_value(false).implicit_value(true); + program.add_argument("-l", arg::log).help("log file (stages output will be saved if provided)"); + program.add_argument("-O", arg::optimize).help("run optimization pass").default_value(false).implicit_value(true); #ifdef ENABLE_CODEGEN - program.add_argument("-c", ARG_COMPILE) + program.add_argument("-c", arg::compile) .help("produce an executable instead of LLVM IR code") .default_value(false) .implicit_value(true); - program.add_argument(ARG_CLANG) + program.add_argument(arg::clang) .help("path to clang executable (required if --compile argument is set)") .default_value("clang"); - program.add_argument(ARG_LLC) + program.add_argument(arg::llc) .help("path to llc executable (required if --compile argument is set)") .default_value("llc"); - program.add_argument("-o", ARG_OUTPUT).help("output file"); + program.add_argument("-o", arg::output).help("output file"); #endif - program.add_argument(ARG_FILES).help("source files (separated by spaces)").required().remaining(); + program.add_argument(arg::time) + .help("print execution times of each stage") + .default_value(false) + .implicit_value(true); + program.add_argument(arg::stopAfter) + .help("stop processing after specific stage") + .choices(stage::preprocessor, stage::lexer, stage::parser, stage::semantizer, stage::optimizer); + program.add_argument(arg::files) + .help("source files (separated by spaces)") + .required() + .nargs(argparse::nargs_pattern::at_least_one); try { program.parse_args(argc, argv); } catch (const std::runtime_error &err) { - std::cerr << err.what() << std::endl; + std::cerr << err.what() << "\n"; std::cerr << program; return 1; } - if (program.get(ARG_HELP)) { + if (program.get(arg::help)) { std::cout << program; return 0; } Logger logger; - bool verbose = program.get(ARG_VERBOSE); + bool verbose = program.get(arg::verbose); if (verbose) logger.setStdoutEnabled(true); - if (program.is_used(ARG_LOG)) - logger.setOutputFile(program.get(ARG_LOG)); + if (program.is_used(arg::log)) + logger.setOutputFile(program.get(arg::log)); std::vector files; try { - files = program.get>(ARG_FILES); - logger << files.size() << " file(s) provided:" << std::endl; + files = program.get>(arg::files); + logger << files.size() << " file(s) provided:\n"; for (auto &file : files) - logger << " " << file << std::endl; + logger << " " << file << "\n"; } catch (std::logic_error &e) { - std::cerr << "No files provided" << std::endl; + std::cerr << "No files provided\n"; return 2; } SourceFile source; - for (const std::string &path : files) { - SourceFile other = utils::readFile(path); - source.insert(source.end(), std::make_move_iterator(other.begin()), std::make_move_iterator(other.end())); - logger << "Read file " << path << std::endl; + try { + for (const std::string &path : files) { + SourceFile other = utils::readFile(path); + source.insert(source.end(), std::make_move_iterator(other.begin()), std::make_move_iterator(other.end())); + logger << "Read file " << path << "\n"; + } + } catch (std::exception &e) { + logger << e.what(); + return 3; } + std::string stopAfter; + if (program.is_used(arg::stopAfter)) + stopAfter = program.get(arg::stopAfter); + bool times = program.get(arg::time); + Timer timer; + std::vector measuredTimes; + ast::SyntaxTree tree; try { - logger << std::endl << "PREPROCESSOR:" << std::endl; + logger << "\nPREPROCESSOR:\n"; + timer.start(); source = Preprocessor::process(source); - logger << dumping::dump(source) << std::endl; - - logger << "LEXER:" << std::endl; + timer.stop(); + measuredTimes.push_back(timer.elapsed()); + if (times) + logger << "PREPROCESSOR Elapsed time: " << measuredTimes.back() << "\n"; + logger << dumping::dump(source) << "\n"; + if (stopAfter == stage::preprocessor) + return 0; + + logger << "\nLEXER:\n"; + timer.start(); auto tokens = Lexer::process(source); - logger << dumping::dump(tokens) << std::endl; - - logger << "PARSER:" << std::endl; + timer.stop(); + measuredTimes.push_back(timer.elapsed()); + if (times) + logger << "LEXER Elapsed time: " << measuredTimes.back() << "\n"; + logger << dumping::dump(tokens) << "\n"; + if (stopAfter == stage::lexer) + return 0; + + logger << "PARSER:\n"; + timer.start(); tree = Parser::process(tokens); + timer.stop(); + measuredTimes.push_back(timer.elapsed()); + if (times) + logger << "PARSER Elapsed time: " << measuredTimes.back() << "\n"; logger << tree.dump(); + if (stopAfter == stage::parser) + return 0; - logger << "SEMANTIZER:" << std::endl; + logger << "SEMANTIZER:\n"; + timer.start(); Semantizer::process(tree); + timer.stop(); + measuredTimes.push_back(timer.elapsed()); + if (times) + logger << "SEMANTIZER Elapsed time: " << measuredTimes.back() << "\n"; logger << tree.dump(); + if (stopAfter == stage::semantizer) + return 0; - if (program.get(ARG_OPTIMIZE)) { - logger << "OPTIMIZER:" << std::endl; + if (program.get(arg::optimize)) { + logger << "OPTIMIZER:\n"; + timer.start(); Optimizer::process(tree); + timer.stop(); + measuredTimes.push_back(timer.elapsed()); + if (times) + logger << "OPTIMIZER Elapsed time: " << measuredTimes.back() << "\n"; logger << tree.dump(); } + + if (stopAfter == stage::optimizer) + return 0; + if (times) + logger << "Compile time: " << std::accumulate(measuredTimes.begin(), measuredTimes.end(), 0LL) << "\n"; } catch (ErrorBuffer &errors) { std::cerr << errors.message(); return 3; } #ifdef ENABLE_CODEGEN - logger << std::endl << "IR GENERATOR:" << std::endl; + logger << "\nIR GENERATOR:\n"; IRGenerator generator("module"); generator.process(tree); if (verbose) { generator.dump(); } - if (!program.is_used(ARG_OUTPUT)) + if (!program.is_used(arg::output)) return 0; - bool compile = program.get(ARG_COMPILE); - std::string output = program.get(ARG_OUTPUT); + bool compile = program.get(arg::compile); + std::string output = program.get(arg::output); if (!compile) { generator.writeToFile(output); return 0; } - std::string llcBin = program.is_used(ARG_LLC) ? program.get(ARG_LLC) : "llc"; - std::string clangBin = program.is_used(ARG_CLANG) ? program.get(ARG_CLANG) : "clang"; + std::string llcBin = program.is_used(arg::llc) ? program.get(arg::llc) : "llc"; + std::string clangBin = program.is_used(arg::clang) ? program.get(arg::clang) : "clang"; try { std::filesystem::path tempDir = createTemporaryDirectory(); @@ -212,7 +298,9 @@ int Compiler::exec(int argc, char *argv[]) { const auto exeFile = tempDir / "out.exe"; std::string llcCmd = generateLlcCommand(llcBin, llFile, objFile); std::string clangCmd = generateClangCommand(clangBin, objFile, exeFile); - logger << "Executing commands:" << std::endl << " " << llcCmd << std::endl << " " << clangCmd << std::endl; + logger << "Executing commands:\n" + << " " << llcCmd << "\n" + << " " << clangCmd << "\n"; bool cmdFailed = (std::system(llcCmd.c_str()) || std::system(clangCmd.c_str())); if (!cmdFailed) std::filesystem::copy_file(exeFile, output); @@ -223,6 +311,8 @@ int Compiler::exec(int argc, char *argv[]) { std::cerr << e.what(); return 3; } + if (stopAfter == stage::codegen) + return 0; #endif return 0; diff --git a/compiler/lib/cli/logger.cpp b/compiler/lib/cli/logger.cpp index a69a3833..220b6b1f 100644 --- a/compiler/lib/cli/logger.cpp +++ b/compiler/lib/cli/logger.cpp @@ -3,13 +3,6 @@ #include #include -#define OUTPUT_OPERATOR_IMPL(str) \ - if (stdoutEnabled) \ - std::cout << (str); \ - if (fileOutput.is_open()) \ - fileOutput << (str); \ - return *this - Logger::Logger() : stdoutEnabled(false) { assert(!fileOutput.is_open()); } @@ -31,23 +24,3 @@ void Logger::closeOutputFile() { if (fileOutput.is_open()) fileOutput.close(); } - -Logger &Logger::operator<<(const std::string &str) { - OUTPUT_OPERATOR_IMPL(str); -} - -Logger &Logger::operator<<(const char *const str) { - OUTPUT_OPERATOR_IMPL(str); -} - -Logger &Logger::operator<<(char chr) { - OUTPUT_OPERATOR_IMPL(chr); -} - -Logger &Logger::operator<<(size_t number) { - OUTPUT_OPERATOR_IMPL(number); -} - -Logger &Logger::operator<<(std::ostream &(*manip)(std::ostream &)) { - OUTPUT_OPERATOR_IMPL(manip); -}