diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 4791087b5..f281439bf 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -15,6 +15,8 @@ install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}") add_executable(yul-phaser yulPhaser/main.cpp + yulPhaser/Phaser.h + yulPhaser/Phaser.cpp yulPhaser/GeneticAlgorithms.h yulPhaser/GeneticAlgorithms.cpp yulPhaser/Population.h diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp new file mode 100644 index 000000000..a1a955d57 --- /dev/null +++ b/tools/yulPhaser/Phaser.cpp @@ -0,0 +1,218 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +using namespace std; +using namespace solidity; +using namespace solidity::langutil; +using namespace solidity::util; +using namespace solidity::phaser; + +namespace po = boost::program_options; + +istream& phaser::operator>>(istream& _inputStream, Algorithm& _algorithm) +{ + string value; + _inputStream >> value; + + if (value == "random") + _algorithm = Algorithm::Random; + else if (value == "GEWEP") + _algorithm = Algorithm::GEWEP; + else + _inputStream.setstate(ios_base::failbit); + + return _inputStream; +} + +ostream& phaser::operator<<(ostream& _outputStream, Algorithm _algorithm) +{ + if (_algorithm == Algorithm::Random) + _outputStream << "random"; + else if (_algorithm == Algorithm::GEWEP) + _outputStream << "GEWEP"; + else + _outputStream.setstate(ios_base::failbit); + + return _outputStream; +} + +namespace +{ + +CharStream loadSource(string const& _sourcePath) +{ + assertThrow(boost::filesystem::exists(_sourcePath), InvalidProgram, "Source file does not exist"); + + string sourceCode = readFileAsString(_sourcePath); + return CharStream(sourceCode, _sourcePath); +} + +} + +int Phaser::main(int _argc, char** _argv) +{ + CommandLineParsingResult parsingResult = parseCommandLine(_argc, _argv); + if (parsingResult.exitCode != 0) + return parsingResult.exitCode; + + initialiseRNG(parsingResult.arguments); + + runAlgorithm( + parsingResult.arguments["input-file"].as(), + parsingResult.arguments["algorithm"].as() + ); + return 0; +} + +Phaser::CommandLineParsingResult Phaser::parseCommandLine(int _argc, char** _argv) +{ + po::options_description description( + "yul-phaser, a tool for finding the best sequence of Yul optimisation phases.\n" + "\n" + "Usage: yul-phaser [options] \n" + "Reads as Yul code and tries to find the best order in which to run optimisation" + " phases using a genetic algorithm.\n" + "Example:\n" + "yul-phaser program.yul\n" + "\n" + "Allowed options", + po::options_description::m_default_line_length, + po::options_description::m_default_line_length - 23 + ); + + description.add_options() + ("help", "Show help message and exit.") + ("input-file", po::value()->required(), "Input file") + ("seed", po::value(), "Seed for the random number generator") + ( + "algorithm", + po::value()->default_value(Algorithm::GEWEP), + "Algorithm" + ) + ; + + po::positional_options_description positionalDescription; + po::variables_map arguments; + positionalDescription.add("input-file", 1); + po::notify(arguments); + + try + { + po::command_line_parser parser(_argc, _argv); + parser.options(description).positional(positionalDescription); + po::store(parser.run(), arguments); + } + catch (po::error const & _exception) + { + cerr << _exception.what() << endl; + return {1, move(arguments)}; + } + + if (arguments.count("help") > 0) + { + cout << description << endl; + return {2, move(arguments)}; + } + + if (arguments.count("input-file") == 0) + { + cerr << "Missing argument: input-file." << endl; + return {1, move(arguments)}; + } + + return {0, arguments}; +} + +void Phaser::initialiseRNG(po::variables_map const& _arguments) +{ + uint32_t seed; + if (_arguments.count("seed") > 0) + seed = _arguments["seed"].as(); + else + seed = SimulationRNG::generateSeed(); + + SimulationRNG::reset(seed); + cout << "Random seed: " << seed << endl; +} + +void Phaser::runAlgorithm(string const& _sourcePath, Algorithm _algorithm) +{ + constexpr size_t populationSize = 20; + constexpr size_t minChromosomeLength = 12; + constexpr size_t maxChromosomeLength = 30; + + CharStream sourceCode = loadSource(_sourcePath); + shared_ptr fitnessMetric = make_shared(Program::load(sourceCode), 5); + auto population = Population::makeRandom( + fitnessMetric, + populationSize, + minChromosomeLength, + maxChromosomeLength + ); + + switch (_algorithm) + { + case Algorithm::Random: + { + RandomAlgorithm( + population, + cout, + { + /* elitePoolSize = */ 1.0 / populationSize, + /* minChromosomeLength = */ minChromosomeLength, + /* maxChromosomeLength = */ maxChromosomeLength, + } + ).run(); + + break; + } + case Algorithm::GEWEP: + { + GenerationalElitistWithExclusivePools( + population, + cout, + { + /* mutationPoolSize = */ 0.25, + /* crossoverPoolSize = */ 0.25, + /* randomisationChance = */ 0.9, + /* deletionVsAdditionChance = */ 0.5, + /* percentGenesToRandomise = */ 1.0 / maxChromosomeLength, + /* percentGenesToAddOrDelete = */ 1.0 / maxChromosomeLength, + } + ).run(); + + break; + } + } +} diff --git a/tools/yulPhaser/Phaser.h b/tools/yulPhaser/Phaser.h new file mode 100644 index 000000000..65a82eba4 --- /dev/null +++ b/tools/yulPhaser/Phaser.h @@ -0,0 +1,64 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Contains the main class that controls yul-phaser based on command-line parameters. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace solidity::phaser +{ + +enum class Algorithm +{ + Random, + GEWEP, +}; + +std::istream& operator>>(std::istream& _inputStream, solidity::phaser::Algorithm& _algorithm); +std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::Algorithm _algorithm); + +/** + * Main class that controls yul-phaser based on command-line parameters. The class is responsible + * for command-line parsing, initialisation of global objects (like the random number generator), + * creating instances of main components and running the genetic algorithm. + */ +class Phaser +{ +public: + static int main(int argc, char** argv); + +private: + struct CommandLineParsingResult + { + int exitCode; + boost::program_options::variables_map arguments; + }; + + static CommandLineParsingResult parseCommandLine(int _argc, char** _argv); + static void initialiseRNG(boost::program_options::variables_map const& _arguments); + + static void runAlgorithm(std::string const& _sourcePath, Algorithm _algorithm); +}; + +} diff --git a/tools/yulPhaser/main.cpp b/tools/yulPhaser/main.cpp index 557a1f74e..eecaf6d3c 100644 --- a/tools/yulPhaser/main.cpp +++ b/tools/yulPhaser/main.cpp @@ -16,224 +16,19 @@ */ #include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include +#include #include -#include - -using namespace std; -using namespace solidity::langutil; -using namespace solidity::phaser; -using namespace solidity::util; - -namespace po = boost::program_options; - -enum class Algorithm -{ - Random, - GEWEP -}; - -istream& operator>>(istream& _inputStream, Algorithm& _algorithm) -{ - string value; - _inputStream >> value; - - if (value == "random") - _algorithm = Algorithm::Random; - else if (value == "GEWEP") - _algorithm = Algorithm::GEWEP; - else - _inputStream.setstate(ios_base::failbit); - - return _inputStream; -} - -ostream& operator<<(ostream& _outputStream, Algorithm _algorithm) -{ - if (_algorithm == Algorithm::Random) - _outputStream << "random"; - else if (_algorithm == Algorithm::GEWEP) - _outputStream << "GEWEP"; - else - _outputStream.setstate(ios_base::failbit); - - return _outputStream; -} - -namespace -{ - -struct CommandLineParsingResult -{ - int exitCode; - po::variables_map arguments; -}; - - -void initialiseRNG(po::variables_map const& _arguments) -{ - uint32_t seed; - if (_arguments.count("seed") > 0) - seed = _arguments["seed"].as(); - else - seed = SimulationRNG::generateSeed(); - - SimulationRNG::reset(seed); - cout << "Random seed: " << seed << endl; -} - -CharStream loadSource(string const& _sourcePath) -{ - assertThrow(boost::filesystem::exists(_sourcePath), InvalidProgram, "Source file does not exist"); - - string sourceCode = readFileAsString(_sourcePath); - return CharStream(sourceCode, _sourcePath); -} - -void runAlgorithm(string const& _sourcePath, Algorithm _algorithm) -{ - constexpr size_t populationSize = 20; - constexpr size_t minChromosomeLength = 12; - constexpr size_t maxChromosomeLength = 30; - - CharStream sourceCode = loadSource(_sourcePath); - shared_ptr fitnessMetric = make_shared(Program::load(sourceCode), 5); - auto population = Population::makeRandom( - fitnessMetric, - populationSize, - minChromosomeLength, - maxChromosomeLength - ); - - switch (_algorithm) - { - case Algorithm::Random: - { - RandomAlgorithm( - population, - cout, - { - /* elitePoolSize = */ 1.0 / populationSize, - /* minChromosomeLength = */ minChromosomeLength, - /* maxChromosomeLength = */ maxChromosomeLength, - } - ).run(); - - break; - } - case Algorithm::GEWEP: - { - GenerationalElitistWithExclusivePools( - population, - cout, - { - /* mutationPoolSize = */ 0.25, - /* crossoverPoolSize = */ 0.25, - /* randomisationChance = */ 0.9, - /* deletionVsAdditionChance = */ 0.5, - /* percentGenesToRandomise = */ 1.0 / maxChromosomeLength, - /* percentGenesToAddOrDelete = */ 1.0 / maxChromosomeLength, - } - ).run(); - - break; - } - } -} - -CommandLineParsingResult parseCommandLine(int _argc, char** _argv) -{ - po::options_description description( - "yul-phaser, a tool for finding the best sequence of Yul optimisation phases.\n" - "\n" - "Usage: yul-phaser [options] \n" - "Reads as Yul code and tries to find the best order in which to run optimisation" - " phases using a genetic algorithm.\n" - "Example:\n" - "yul-phaser program.yul\n" - "\n" - "Allowed options", - po::options_description::m_default_line_length, - po::options_description::m_default_line_length - 23 - ); - - description.add_options() - ("help", "Show help message and exit.") - ("input-file", po::value()->required(), "Input file") - ("seed", po::value(), "Seed for the random number generator") - ( - "algorithm", - po::value()->default_value(Algorithm::GEWEP), - "Algorithm" - ) - ; - - po::positional_options_description positionalDescription; - po::variables_map arguments; - positionalDescription.add("input-file", 1); - po::notify(arguments); - - try - { - po::command_line_parser parser(_argc, _argv); - parser.options(description).positional(positionalDescription); - po::store(parser.run(), arguments); - } - catch (po::error const & _exception) - { - cerr << _exception.what() << endl; - return {1, move(arguments)}; - } - - if (arguments.count("help") > 0) - { - cout << description << endl; - return {2, move(arguments)}; - } - - if (arguments.count("input-file") == 0) - { - cerr << "Missing argument: input-file." << endl; - return {1, move(arguments)}; - } - - return {0, arguments}; -} - -} int main(int argc, char** argv) { - CommandLineParsingResult parsingResult = parseCommandLine(argc, argv); - if (parsingResult.exitCode != 0) - return parsingResult.exitCode; - - initialiseRNG(parsingResult.arguments); - try { - runAlgorithm( - parsingResult.arguments["input-file"].as(), - parsingResult.arguments["algorithm"].as() - ); + return solidity::phaser::Phaser::main(argc, argv); } - catch (InvalidProgram const& _exception) + catch (solidity::phaser::InvalidProgram const& exception) { - cerr << "ERROR: " << _exception.what() << endl; + std::cerr << "ERROR: " << exception.what() << std::endl; return 1; } - - return 0; }