Merge pull request #8223 from imapp-pl/yul-phaser-program-reuse

[yul-phaser] Program reuse
This commit is contained in:
chriseth 2020-02-06 13:29:44 +01:00 committed by GitHub
commit f0ebb26f29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 69 additions and 39 deletions

View File

@ -17,6 +17,7 @@
#include <tools/yulPhaser/Chromosome.h>
#include <tools/yulPhaser/Population.h>
#include <tools/yulPhaser/Program.h>
#include <libyul/optimiser/BlockFlattener.h>
#include <libyul/optimiser/SSAReverser.h>
@ -85,7 +86,7 @@ BOOST_AUTO_TEST_CASE(constructor_should_copy_chromosomes_and_not_compute_fitness
Chromosome::makeRandom(5),
Chromosome::makeRandom(10),
};
Population population(sourceStream, chromosomes);
Population population(Program::load(sourceStream), chromosomes);
BOOST_TEST(population.individuals().size() == 2);
BOOST_TEST(population.individuals()[0].chromosome == chromosomes[0]);
@ -98,8 +99,9 @@ BOOST_AUTO_TEST_CASE(constructor_should_copy_chromosomes_and_not_compute_fitness
BOOST_AUTO_TEST_CASE(makeRandom_should_return_population_with_random_chromosomes)
{
CharStream sourceStream(sampleSourceCode, current_test_case().p_name);
auto population1 = Population::makeRandom(sourceStream, 100);
auto population2 = Population::makeRandom(sourceStream, 100);
auto program = Program::load(sourceStream);
auto population1 = Population::makeRandom(program, 100);
auto population2 = Population::makeRandom(program, 100);
BOOST_TEST(population1.individuals().size() == 100);
BOOST_TEST(population2.individuals().size() == 100);
@ -118,7 +120,7 @@ BOOST_AUTO_TEST_CASE(makeRandom_should_return_population_with_random_chromosomes
BOOST_AUTO_TEST_CASE(makeRandom_should_not_compute_fitness)
{
CharStream sourceStream(sampleSourceCode, current_test_case().p_name);
auto population = Population::makeRandom(sourceStream, 5);
auto population = Population::makeRandom(Program::load(sourceStream), 5);
BOOST_TEST(all_of(population.individuals().begin(), population.individuals().end(), fitnessNotSet));
}
@ -127,7 +129,7 @@ BOOST_AUTO_TEST_CASE(run_should_evaluate_fitness)
{
stringstream output;
CharStream sourceStream(sampleSourceCode, current_test_case().p_name);
auto population = Population::makeRandom(sourceStream, 5);
auto population = Population::makeRandom(Program::load(sourceStream), 5);
assert(all_of(population.individuals().begin(), population.individuals().end(), fitnessNotSet));
population.run(1, output);
@ -146,11 +148,12 @@ BOOST_AUTO_TEST_CASE(run_should_not_make_fitness_of_top_chromosomes_worse)
Chromosome({UnusedPruner::name}),
Chromosome({StructuralSimplifier::name, BlockFlattener::name}),
};
Population population(sourceStream, chromosomes);
auto program = Program::load(sourceStream);
Population population(program, chromosomes);
size_t initialTopFitness[2] = {
Population::measureFitness(chromosomes[0], sourceStream),
Population::measureFitness(chromosomes[1], sourceStream),
Population::measureFitness(chromosomes[0], program),
Population::measureFitness(chromosomes[1], program),
};
for (int i = 0; i < 6; ++i)

View File

@ -65,6 +65,26 @@ namespace solidity::phaser::test
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(ProgramTest)
BOOST_AUTO_TEST_CASE(copy_constructor_should_make_deep_copy_of_ast)
{
string sourceCode(
"{\n"
" let x := 1\n"
"}\n"
);
CharStream sourceStream(sourceCode, current_test_case().p_name);
auto program = Program::load(sourceStream);
Program programCopy(program);
BOOST_TEST(&programCopy.ast() != &program.ast());
// There might be a more direct way to compare ASTs but converting to JSON should be good enough
// as long as the conversion is deterministic. A very nice side effect of doing it this way is
// that BOOST_TEST will print the complete AST structure of both programs in case of a mismatch.
BOOST_TEST(programCopy.toJson() == program.toJson());
}
BOOST_AUTO_TEST_CASE(load_should_rewind_the_stream)
{
string sourceCode(

View File

@ -49,27 +49,27 @@ ostream& phaser::operator<<(ostream& _stream, Individual const& _individual)
return _stream;
}
Population::Population(CharStream _sourceCode, vector<Chromosome> const& _chromosomes):
m_sourceCode{move(_sourceCode)}
Population::Population(Program _program, vector<Chromosome> const& _chromosomes):
m_program{move(_program)}
{
for (auto const& chromosome: _chromosomes)
m_individuals.push_back({chromosome});
}
Population Population::makeRandom(CharStream _sourceCode, size_t _size)
Population Population::makeRandom(Program _program, size_t _size)
{
vector<Individual> individuals;
for (size_t i = 0; i < _size; ++i)
individuals.push_back({Chromosome::makeRandom(randomChromosomeLength())});
return Population(move(_sourceCode), individuals);
return Population(move(_program), individuals);
}
size_t Population::measureFitness(Chromosome const& _chromosome, CharStream& _sourceCode)
size_t Population::measureFitness(Chromosome const& _chromosome, Program const& _program)
{
auto program = Program::load(_sourceCode);
program.optimise(_chromosome.optimisationSteps());
return program.codeSize();
Program programCopy = _program;
programCopy.optimise(_chromosome.optimisationSteps());
return programCopy.codeSize();
}
void Population::run(optional<size_t> _numRounds, ostream& _outputStream)
@ -88,8 +88,6 @@ void Population::run(optional<size_t> _numRounds, ostream& _outputStream)
ostream& phaser::operator<<(ostream& _stream, Population const& _population)
{
_stream << "Stream name: " << _population.m_sourceCode.name() << endl;
auto individual = _population.m_individuals.begin();
for (; individual != _population.m_individuals.end(); ++individual)
_stream << *individual << endl;
@ -106,7 +104,7 @@ void Population::doEvaluation()
{
for (auto& individual: m_individuals)
if (!individual.fitness.has_value())
individual.fitness = measureFitness(individual.chromosome, m_sourceCode);
individual.fitness = measureFitness(individual.chromosome, m_program);
}
void Population::doSelection()

View File

@ -18,10 +18,9 @@
#pragma once
#include <tools/yulPhaser/Chromosome.h>
#include <tools/yulPhaser/Program.h>
#include <tools/yulPhaser/Random.h>
#include <liblangutil/CharStream.h>
#include <optional>
#include <ostream>
#include <vector>
@ -47,7 +46,7 @@ struct Individual
* and selecting the best ones for the next round.
*
* An individual is a sequence of optimiser steps represented by a @a Chromosome instance. The whole
* population is associated with a fixed Yul program. By loading the source code into a @a Program
* population is associated with a fixed Yul program. By applying the steps to the @a Program
* instance the class can compute fitness of the individual.
*/
class Population
@ -55,21 +54,21 @@ class Population
public:
static constexpr size_t MaxChromosomeLength = 30;
explicit Population(langutil::CharStream _sourceCode, std::vector<Chromosome> const& _chromosomes = {});
static Population makeRandom(langutil::CharStream _sourceCode, size_t _size);
explicit Population(Program _program, std::vector<Chromosome> const& _chromosomes = {});
static Population makeRandom(Program _program, size_t _size);
void run(std::optional<size_t> _numRounds, std::ostream& _outputStream);
std::vector<Individual> const& individuals() const { return m_individuals; }
static size_t randomChromosomeLength() { return binomialRandomInt(MaxChromosomeLength, 0.5); }
static size_t measureFitness(Chromosome const& _chromosome, langutil::CharStream& _sourceCode);
static size_t measureFitness(Chromosome const& _chromosome, Program const& _program);
friend std::ostream& operator<<(std::ostream& _stream, Population const& _population);
private:
explicit Population(langutil::CharStream _sourceCode, std::vector<Individual> _individuals = {}):
m_sourceCode{std::move(_sourceCode)},
explicit Population(Program _program, std::vector<Individual> _individuals = {}):
m_program{std::move(_program)},
m_individuals{std::move(_individuals)} {}
void doMutation();
@ -81,8 +80,7 @@ private:
size_t _count
);
langutil::CharStream m_sourceCode;
Program m_program;
std::vector<Individual> m_individuals;
};

View File

@ -57,6 +57,13 @@ ostream& operator<<(ostream& _stream, Program const& _program);
}
Program::Program(Program const& program):
m_ast(make_unique<Block>(get<Block>(ASTCopier{}(*program.m_ast)))),
m_dialect{program.m_dialect},
m_nameDispenser(program.m_nameDispenser)
{
}
Program Program::load(CharStream& _sourceCode)
{
// ASSUMPTION: parseSource() rewinds the stream on its own
@ -79,7 +86,7 @@ Program Program::load(CharStream& _sourceCode)
void Program::optimise(vector<string> const& _optimisationSteps)
{
applyOptimisationSteps(m_dialect, m_nameDispenser, *m_ast, _optimisationSteps);
m_ast = applyOptimisationSteps(m_dialect, m_nameDispenser, move(m_ast), _optimisationSteps);
}
ostream& phaser::operator<<(ostream& _stream, Program const& _program)
@ -133,10 +140,10 @@ unique_ptr<Block> Program::disambiguateAST(
return make_unique<Block>(get<Block>(disambiguator(_ast)));
}
void Program::applyOptimisationSteps(
unique_ptr<Block> Program::applyOptimisationSteps(
Dialect const& _dialect,
NameDispenser& _nameDispenser,
Block& _ast,
unique_ptr<Block> _ast,
vector<string> const& _optimisationSteps
)
{
@ -146,7 +153,9 @@ void Program::applyOptimisationSteps(
OptimiserStepContext context{_dialect, _nameDispenser, externallyUsedIdentifiers};
for (string const& step: _optimisationSteps)
OptimiserSuite::allSteps().at(step)->run(context, _ast);
OptimiserSuite::allSteps().at(step)->run(context, *_ast);
return _ast;
}
size_t Program::computeCodeSize(Block const& _ast)

View File

@ -20,8 +20,6 @@
#include <libyul/optimiser/NameDispenser.h>
#include <libyul/AsmData.h>
#include <boost/noncopyable.hpp>
#include <optional>
#include <ostream>
#include <set>
@ -55,14 +53,16 @@ namespace solidity::phaser
* The class allows the user to apply extra optimisations and obtain metrics and general
* information about the resulting syntax tree.
*/
class Program: private boost::noncopyable
class Program
{
public:
Program(Program const& program);
Program(Program&& program):
m_ast(std::move(program.m_ast)),
m_dialect{program.m_dialect},
m_nameDispenser(std::move(program.m_nameDispenser))
{}
Program operator=(Program const& program) = delete;
Program operator=(Program&& program) = delete;
static Program load(langutil::CharStream& _sourceCode);
@ -97,10 +97,10 @@ private:
yul::Block const& _ast,
yul::AsmAnalysisInfo const& _analysisInfo
);
static void applyOptimisationSteps(
static std::unique_ptr<yul::Block> applyOptimisationSteps(
yul::Dialect const& _dialect,
yul::NameDispenser& _nameDispenser,
yul::Block& _ast,
std::unique_ptr<yul::Block> _ast,
std::vector<std::string> const& _optimisationSteps
);
static size_t computeCodeSize(yul::Block const& _ast);

View File

@ -17,6 +17,7 @@
#include <tools/yulPhaser/Exceptions.h>
#include <tools/yulPhaser/Population.h>
#include <tools/yulPhaser/Program.h>
#include <libsolutil/Assertions.h>
#include <libsolutil/CommonIO.h>
@ -54,7 +55,8 @@ CharStream loadSource(string const& _sourcePath)
void runAlgorithm(string const& _sourcePath)
{
auto population = Population::makeRandom(loadSource(_sourcePath), 10);
CharStream sourceCode = loadSource(_sourcePath);
auto population = Population::makeRandom(Program::load(sourceCode), 10);
population.run(nullopt, cout);
}