/* 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 #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; namespace { map const AlgorithmToStringMap = { {Algorithm::Random, "random"}, {Algorithm::GEWEP, "GEWEP"}, }; map const StringToAlgorithmMap = invertMap(AlgorithmToStringMap); } istream& phaser::operator>>(istream& _inputStream, Algorithm& _algorithm) { return deserializeChoice(_inputStream, _algorithm, StringToAlgorithmMap); } ostream& phaser::operator<<(ostream& _outputStream, Algorithm _algorithm) { return serializeChoice(_outputStream, _algorithm, AlgorithmToStringMap); } GeneticAlgorithmFactory::Options GeneticAlgorithmFactory::Options::fromCommandLine(po::variables_map const& _arguments) { return { _arguments["algorithm"].as(), }; } unique_ptr GeneticAlgorithmFactory::build( Options const& _options, size_t _populationSize, size_t _minChromosomeLength, size_t _maxChromosomeLength ) { assert(_populationSize > 0); switch (_options.algorithm) { case Algorithm::Random: return make_unique(RandomAlgorithm::Options{ /* elitePoolSize = */ 1.0 / _populationSize, /* minChromosomeLength = */ _minChromosomeLength, /* maxChromosomeLength = */ _maxChromosomeLength, }); case Algorithm::GEWEP: return make_unique(GenerationalElitistWithExclusivePools::Options{ /* mutationPoolSize = */ 0.25, /* crossoverPoolSize = */ 0.25, /* randomisationChance = */ 0.9, /* deletionVsAdditionChance = */ 0.5, /* percentGenesToRandomise = */ 1.0 / _maxChromosomeLength, /* percentGenesToAddOrDelete = */ 1.0 / _maxChromosomeLength, }); default: assertThrow(false, solidity::util::Exception, "Invalid Algorithm value."); } } FitnessMetricFactory::Options FitnessMetricFactory::Options::fromCommandLine(po::variables_map const& _arguments) { return { _arguments["chromosome-repetitions"].as(), }; } unique_ptr FitnessMetricFactory::build( Options const& _options, Program _program ) { return make_unique(move(_program), _options.chromosomeRepetitions); } Population PopulationFactory::build( shared_ptr _fitnessMetric ) { return Population::makeRandom( move(_fitnessMetric), PopulationSize, MinChromosomeLength, MaxChromosomeLength ); } ProgramFactory::Options ProgramFactory::Options::fromCommandLine(po::variables_map const& _arguments) { return { _arguments["input-file"].as(), }; } Program ProgramFactory::build(Options const& _options) { CharStream sourceCode = loadSource(_options.inputFile); variant programOrErrors = Program::load(sourceCode); if (holds_alternative(programOrErrors)) { cerr << get(programOrErrors) << endl; assertThrow(false, InvalidProgram, "Failed to load program " + _options.inputFile); } return move(get(programOrErrors)); } CharStream ProgramFactory::loadSource(string const& _sourcePath) { assertThrow(boost::filesystem::exists(_sourcePath), MissingFile, "Source file does not exist: " + _sourcePath); string sourceCode = readFileAsString(_sourcePath); return CharStream(sourceCode, _sourcePath); } void Phaser::main(int _argc, char** _argv) { optional arguments = parseCommandLine(_argc, _argv); if (!arguments.has_value()) return; initialiseRNG(arguments.value()); runAlgorithm(arguments.value()); } Phaser::CommandLineDescription Phaser::buildCommandLineDescription() { size_t const lineLength = po::options_description::m_default_line_length; size_t const minDescriptionLength = lineLength - 23; po::options_description keywordDescription( "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", lineLength, minDescriptionLength ); po::options_description generalDescription("GENERAL", lineLength, minDescriptionLength); generalDescription.add_options() ("help", "Show help message and exit.") ("input-file", po::value()->required()->value_name(""), "Input file.") ("seed", po::value()->value_name(""), "Seed for the random number generator.") ; keywordDescription.add(generalDescription); po::options_description algorithmDescription("ALGORITHM", lineLength, minDescriptionLength); algorithmDescription.add_options() ( "algorithm", po::value()->value_name("")->default_value(Algorithm::GEWEP), "Algorithm" ) ; keywordDescription.add(algorithmDescription); po::options_description metricsDescription("METRICS", lineLength, minDescriptionLength); metricsDescription.add_options() ( "chromosome-repetitions", po::value()->value_name("")->default_value(1), "Number of times to repeat the sequence optimisation steps represented by a chromosome." ) ; keywordDescription.add(metricsDescription); po::positional_options_description positionalDescription; positionalDescription.add("input-file", 1); return {keywordDescription, positionalDescription}; } optional Phaser::parseCommandLine(int _argc, char** _argv) { auto [keywordDescription, positionalDescription] = buildCommandLineDescription(); po::variables_map arguments; po::notify(arguments); po::command_line_parser parser(_argc, _argv); parser.options(keywordDescription).positional(positionalDescription); po::store(parser.run(), arguments); if (arguments.count("help") > 0) { cout << keywordDescription << endl; return nullopt; } if (arguments.count("input-file") == 0) assertThrow(false, NoInputFiles, "Missing argument: input-file."); return 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(po::variables_map const& _arguments) { auto programOptions = ProgramFactory::Options::fromCommandLine(_arguments); auto metricOptions = FitnessMetricFactory::Options::fromCommandLine(_arguments); auto algorithmOptions = GeneticAlgorithmFactory::Options::fromCommandLine(_arguments); Program program = ProgramFactory::build(programOptions); unique_ptr fitnessMetric = FitnessMetricFactory::build(metricOptions, move(program)); Population population = PopulationFactory::build(move(fitnessMetric)); unique_ptr geneticAlgorithm = GeneticAlgorithmFactory::build( algorithmOptions, population.individuals().size(), PopulationFactory::MinChromosomeLength, PopulationFactory::MaxChromosomeLength ); AlgorithmRunner algorithmRunner(population, AlgorithmRunner::Options{}, cout); algorithmRunner.run(*geneticAlgorithm); }