diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index e7a9d903f..7cf466e0b 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -138,6 +138,22 @@ set(libyul_sources
)
detect_stray_source_files("${libyul_sources}" "libyul/")
+set(yul_phaser_sources
+ yulPhaser/Chromosome.cpp
+ yulPhaser/Population.cpp
+ yulPhaser/Program.cpp
+ yulPhaser/Random.cpp
+
+ # FIXME: yul-phaser is not a library so I can't just add it to target_link_libraries().
+ # My current workaround is just to include its source files here but this introduces
+ # unnecessary duplication. Create a library or find a way to reuse the list in both places.
+ ../tools/yulPhaser/Chromosome.cpp
+ ../tools/yulPhaser/Population.cpp
+ ../tools/yulPhaser/Program.cpp
+ ../tools/yulPhaser/Random.cpp
+)
+detect_stray_source_files("${yul_phaser_sources}" "yulPhaser/")
+
add_executable(soltest ${sources}
${contracts_sources}
${libsolutil_sources}
@@ -146,6 +162,7 @@ add_executable(soltest ${sources}
${libyul_sources}
${libsolidity_sources}
${libsolidity_util_sources}
+ ${yul_phaser_sources}
)
target_link_libraries(soltest PRIVATE libsolc yul solidity yulInterpreter evmasm solutil Boost::boost Boost::program_options Boost::unit_test_framework evmc)
diff --git a/test/yulPhaser/Chromosome.cpp b/test/yulPhaser/Chromosome.cpp
new file mode 100644
index 000000000..dd250d0ec
--- /dev/null
+++ b/test/yulPhaser/Chromosome.cpp
@@ -0,0 +1,101 @@
+/*
+ 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
+
+using namespace std;
+using namespace solidity::yul;
+using namespace solidity::util;
+
+namespace solidity::phaser::test
+{
+
+BOOST_AUTO_TEST_SUITE(Phaser)
+BOOST_AUTO_TEST_SUITE(ChromosomeTest)
+
+BOOST_AUTO_TEST_CASE(makeRandom_should_create_chromosome_with_random_optimisation_steps)
+{
+ constexpr uint32_t numSteps = 1000;
+
+ auto chromosome1 = Chromosome::makeRandom(numSteps);
+ auto chromosome2 = Chromosome::makeRandom(numSteps);
+ BOOST_CHECK_EQUAL(chromosome1.length(), numSteps);
+ BOOST_CHECK_EQUAL(chromosome2.length(), numSteps);
+
+ multiset steps1;
+ multiset steps2;
+ for (auto const& step: chromosome1.optimisationSteps())
+ steps1.insert(step);
+ for (auto const& step: chromosome2.optimisationSteps())
+ steps2.insert(step);
+
+ // Check if steps are different and also if they're not just a permutation of the same set.
+ // Technically they could be the same and still random but the probability is infinitesimally low.
+ BOOST_TEST(steps1 != steps2);
+}
+
+BOOST_AUTO_TEST_CASE(constructor_should_store_optimisation_steps)
+{
+ vector steps = {
+ StructuralSimplifier::name,
+ BlockFlattener::name,
+ UnusedPruner::name,
+ };
+ Chromosome chromosome(steps);
+
+ BOOST_TEST(steps == chromosome.optimisationSteps());
+}
+
+BOOST_AUTO_TEST_CASE(constructor_should_allow_duplicate_steps)
+{
+ vector steps = {
+ StructuralSimplifier::name,
+ StructuralSimplifier::name,
+ BlockFlattener::name,
+ UnusedPruner::name,
+ BlockFlattener::name,
+ };
+ Chromosome chromosome(steps);
+
+ BOOST_TEST(steps == chromosome.optimisationSteps());
+}
+
+BOOST_AUTO_TEST_CASE(output_operator_should_create_concise_and_unambiguous_string_representation)
+{
+ vector allSteps;
+ for (auto const& step: OptimiserSuite::allSteps())
+ allSteps.push_back(step.first);
+ Chromosome chromosome(allSteps);
+
+ BOOST_TEST(chromosome.length() == allSteps.size());
+ BOOST_TEST(chromosome.optimisationSteps() == allSteps);
+ BOOST_TEST(toString(chromosome) == "fcCUnDvejsxIOoighTLMrmVatud");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END()
+
+}
diff --git a/test/yulPhaser/Population.cpp b/test/yulPhaser/Population.cpp
new file mode 100644
index 000000000..d0ee5a0c7
--- /dev/null
+++ b/test/yulPhaser/Population.cpp
@@ -0,0 +1,176 @@
+/*
+ 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::langutil;
+using namespace solidity::yul;
+using namespace boost::unit_test::framework;
+
+namespace solidity::phaser::test
+{
+
+namespace
+{
+ bool fitnessNotSet(Individual const& individual)
+ {
+ return !individual.fitness.has_value();
+ }
+
+ bool fitnessSet(Individual const& individual)
+ {
+ return individual.fitness.has_value();
+ }
+}
+
+BOOST_AUTO_TEST_SUITE(Phaser)
+BOOST_AUTO_TEST_SUITE(PopulationTest)
+
+string const& sampleSourceCode =
+ "{\n"
+ " let factor := 13\n"
+ " {\n"
+ " if factor\n"
+ " {\n"
+ " let variable := add(1, 2)\n"
+ " }\n"
+ " let result := factor\n"
+ " }\n"
+ " let something := 6\n"
+ " {\n"
+ " {\n"
+ " {\n"
+ " let value := 15\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " let something_else := mul(mul(something, 1), add(factor, 0))\n"
+ " if 1 { let x := 1 }\n"
+ " if 0 { let y := 2 }\n"
+ "}\n";
+
+BOOST_AUTO_TEST_CASE(constructor_should_copy_chromosomes_and_not_compute_fitness)
+{
+ CharStream sourceStream(sampleSourceCode, current_test_case().p_name);
+ vector chromosomes = {
+ Chromosome::makeRandom(5),
+ Chromosome::makeRandom(10),
+ };
+ Population population(sourceStream, chromosomes);
+
+ BOOST_TEST(population.individuals().size() == 2);
+ BOOST_TEST(population.individuals()[0].chromosome == chromosomes[0]);
+ BOOST_TEST(population.individuals()[1].chromosome == chromosomes[1]);
+
+ auto fitnessNotSet = [](auto const& individual){ return !individual.fitness.has_value(); };
+ BOOST_TEST(all_of(population.individuals().begin(), population.individuals().end(), fitnessNotSet));
+}
+
+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);
+
+ BOOST_TEST(population1.individuals().size() == 100);
+ BOOST_TEST(population2.individuals().size() == 100);
+
+ int numMatchingPositions = 0;
+ for (size_t i = 0; i < 100; ++i)
+ if (population1.individuals()[i].chromosome == population2.individuals()[i].chromosome)
+ ++numMatchingPositions;
+
+ // Assume that the results are random if there are no more than 10 identical chromosomes on the
+ // same positions. One duplicate is very unlikely but still possible after billions of runs
+ // (especially for short chromosomes). For ten the probability is so small that we can ignore it.
+ BOOST_TEST(numMatchingPositions < 10);
+}
+
+BOOST_AUTO_TEST_CASE(makeRandom_should_not_compute_fitness)
+{
+ CharStream sourceStream(sampleSourceCode, current_test_case().p_name);
+ auto population = Population::makeRandom(sourceStream, 5);
+
+ BOOST_TEST(all_of(population.individuals().begin(), population.individuals().end(), fitnessNotSet));
+}
+
+BOOST_AUTO_TEST_CASE(run_should_evaluate_fitness)
+{
+ stringstream output;
+ CharStream sourceStream(sampleSourceCode, current_test_case().p_name);
+ auto population = Population::makeRandom(sourceStream, 5);
+ assert(all_of(population.individuals().begin(), population.individuals().end(), fitnessNotSet));
+
+ population.run(1, output);
+
+ BOOST_TEST(all_of(population.individuals().begin(), population.individuals().end(), fitnessSet));
+}
+
+BOOST_AUTO_TEST_CASE(run_should_not_make_fitness_of_top_chromosomes_worse)
+{
+ stringstream output;
+ CharStream sourceStream(sampleSourceCode, current_test_case().p_name);
+ vector chromosomes = {
+ Chromosome({StructuralSimplifier::name}),
+ Chromosome({BlockFlattener::name}),
+ Chromosome({SSAReverser::name}),
+ Chromosome({UnusedPruner::name}),
+ Chromosome({StructuralSimplifier::name, BlockFlattener::name}),
+ };
+ Population population(sourceStream, chromosomes);
+
+ size_t initialTopFitness[2] = {
+ Population::measureFitness(chromosomes[0], sourceStream),
+ Population::measureFitness(chromosomes[1], sourceStream),
+ };
+
+ for (int i = 0; i < 6; ++i)
+ {
+ population.run(1, output);
+ BOOST_TEST(population.individuals().size() == 5);
+ BOOST_TEST(fitnessSet(population.individuals()[0]));
+ BOOST_TEST(fitnessSet(population.individuals()[1]));
+
+ size_t currentTopFitness[2] = {
+ population.individuals()[0].fitness.value(),
+ population.individuals()[1].fitness.value(),
+ };
+ BOOST_TEST(currentTopFitness[0] <= initialTopFitness[0]);
+ BOOST_TEST(currentTopFitness[1] <= initialTopFitness[1]);
+ BOOST_TEST(currentTopFitness[0] <= currentTopFitness[1]);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END()
+
+}
diff --git a/test/yulPhaser/Program.cpp b/test/yulPhaser/Program.cpp
new file mode 100644
index 000000000..26f2934ea
--- /dev/null
+++ b/test/yulPhaser/Program.cpp
@@ -0,0 +1,266 @@
+/*
+ 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
+
+using namespace std;
+using namespace solidity::langutil;
+using namespace solidity::util;
+using namespace solidity::yul;
+using namespace boost::unit_test::framework;
+
+namespace
+{
+ /// If the specified block is redundant (i.e. the only thing it contains is another block)
+ /// the function recurses into it and returns the first non-redundant one it finds.
+ /// If the block isn't redundant it just returns it immediately.
+ Block const& skipRedundantBlocks(Block const& _block)
+ {
+ if (_block.statements.size() == 1 && holds_alternative(_block.statements[0]))
+ return skipRedundantBlocks(get(_block.statements[0]));
+ else
+ return _block;
+ }
+
+ string stripWhitespace(string const& input)
+ {
+ regex whitespaceRegex("\\s+");
+ return regex_replace(input, whitespaceRegex, "");
+ }
+}
+
+namespace solidity::phaser::test
+{
+
+BOOST_AUTO_TEST_SUITE(Phaser)
+BOOST_AUTO_TEST_SUITE(ProgramTest)
+
+BOOST_AUTO_TEST_CASE(load_should_rewind_the_stream)
+{
+ string sourceCode(
+ "{\n"
+ " let x := 1\n"
+ " let y := 2\n"
+ "}\n"
+ );
+ CharStream sourceStream(sourceCode, current_test_case().p_name);
+ sourceStream.setPosition(5);
+
+ auto program = Program::load(sourceStream);
+
+ BOOST_TEST(CodeSize::codeSize(program.ast()) == 2);
+}
+
+BOOST_AUTO_TEST_CASE(load_should_disambiguate)
+{
+ string sourceCode(
+ "{\n"
+ " {\n"
+ " let x := 1\n"
+ " }\n"
+ " {\n"
+ " let x := 2\n"
+ " }\n"
+ "}\n"
+ );
+ CharStream sourceStream(sourceCode, current_test_case().p_name);
+ auto program = Program::load(sourceStream);
+
+ // skipRedundantBlocks() makes the test independent of whether load() includes function grouping or not.
+ Block const& parentBlock = skipRedundantBlocks(program.ast());
+ BOOST_TEST(parentBlock.statements.size() == 2);
+
+ Block const& innerBlock1 = get(parentBlock.statements[0]);
+ Block const& innerBlock2 = get(parentBlock.statements[1]);
+ VariableDeclaration const& declaration1 = get(innerBlock1.statements[0]);
+ VariableDeclaration const& declaration2 = get(innerBlock2.statements[0]);
+
+ BOOST_TEST(declaration1.variables[0].name.str() == "x");
+ BOOST_TEST(declaration2.variables[0].name.str() != "x");
+}
+
+BOOST_AUTO_TEST_CASE(load_should_do_function_grouping_and_hoisting)
+{
+ string sourceCode(
+ "{\n"
+ " function foo() -> result\n"
+ " {\n"
+ " result := 1\n"
+ " }\n"
+ " let x := 1\n"
+ " function bar(a) -> result\n"
+ " {\n"
+ " result := 2\n"
+ " }\n"
+ " let y := 2\n"
+ "}\n"
+ );
+ CharStream sourceStream(sourceCode, current_test_case().p_name);
+ auto program = Program::load(sourceStream);
+
+ BOOST_TEST(program.ast().statements.size() == 3);
+ BOOST_TEST(holds_alternative(program.ast().statements[0]));
+ BOOST_TEST(holds_alternative(program.ast().statements[1]));
+ BOOST_TEST(holds_alternative(program.ast().statements[2]));
+}
+
+BOOST_AUTO_TEST_CASE(load_should_do_loop_init_rewriting)
+{
+ string sourceCode(
+ "{\n"
+ " for { let i := 0 } true {}\n"
+ " {\n"
+ " }\n"
+ "}\n"
+ );
+ CharStream sourceStream(sourceCode, current_test_case().p_name);
+ auto program = Program::load(sourceStream);
+
+ // skipRedundantBlocks() makes the test independent of whether load() includes function grouping or not.
+ Block const& parentBlock = skipRedundantBlocks(program.ast());
+ BOOST_TEST(holds_alternative(parentBlock.statements[0]));
+ BOOST_TEST(holds_alternative(parentBlock.statements[1]));
+}
+
+BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_parsed)
+{
+ string sourceCode("invalid program\n");
+ CharStream sourceStream(sourceCode, current_test_case().p_name);
+
+ BOOST_CHECK_THROW(Program::load(sourceStream), InvalidProgram);
+}
+
+BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_analyzed)
+{
+ // This should be parsed just fine but fail the analysis with:
+ // Error: Variable not found or variable not lvalue.
+ string sourceCode(
+ "{\n"
+ " x := 1\n"
+ "}\n"
+ );
+ CharStream sourceStream(sourceCode, current_test_case().p_name);
+
+ BOOST_CHECK_THROW(Program::load(sourceStream), InvalidProgram);
+}
+
+BOOST_AUTO_TEST_CASE(optimise)
+{
+ string sourceCode(
+ "{\n"
+ " {\n"
+ " if 1 { let x := 1 }\n"
+ " if 0 { let y := 2 }\n"
+ " }\n"
+ "}\n"
+ );
+ CharStream sourceStream(sourceCode, current_test_case().p_name);
+ auto program = Program::load(sourceStream);
+
+ [[maybe_unused]] Block const& parentBlockBefore = skipRedundantBlocks(program.ast());
+ assert(parentBlockBefore.statements.size() == 2);
+ assert(holds_alternative(parentBlockBefore.statements[0]));
+ assert(holds_alternative(parentBlockBefore.statements[1]));
+
+ program.optimise({StructuralSimplifier::name, BlockFlattener::name});
+
+ Block const& parentBlockAfter = program.ast();
+ BOOST_TEST(parentBlockAfter.statements.size() == 1);
+ BOOST_TEST(holds_alternative(parentBlockAfter.statements[0]));
+}
+
+BOOST_AUTO_TEST_CASE(output_operator)
+{
+ string sourceCode(
+ "{\n"
+ " let factor := 13\n"
+ " {\n"
+ " if factor\n"
+ " {\n"
+ " let variable := add(1, 2)\n"
+ " }\n"
+ " let result := factor\n"
+ " }\n"
+ " let something := 6\n"
+ " let something_else := mul(something, factor)\n"
+ "}\n"
+ );
+ CharStream sourceStream(sourceCode, current_test_case().p_name);
+ auto program = Program::load(sourceStream);
+
+ // NOTE: The snippet above was chosen so that the few optimisations applied automatically by load()
+ // as of now do not change the code significantly. If that changes, you may have to update it.
+ BOOST_TEST(stripWhitespace(toString(program)) == stripWhitespace("{" + sourceCode + "}"));
+}
+
+BOOST_AUTO_TEST_CASE(toJson)
+{
+ string sourceCode(
+ "{\n"
+ " let a := 3\n"
+ " if a\n"
+ " {\n"
+ " let abc := add(1, 2)\n"
+ " }\n"
+ "}\n"
+ );
+ CharStream sourceStream(sourceCode, current_test_case().p_name);
+ auto program = Program::load(sourceStream);
+
+ Json::Value parsingResult;
+ string errors;
+ BOOST_TEST(jsonParseStrict(program.toJson(), parsingResult, &errors));
+ BOOST_TEST(errors.empty());
+}
+
+BOOST_AUTO_TEST_CASE(codeSize)
+{
+ string sourceCode(
+ "{\n"
+ " function foo() -> result\n"
+ " {\n"
+ " result := 15\n"
+ " }\n"
+ " let a := 1\n"
+ "}\n"
+ );
+ CharStream sourceStream(sourceCode, current_test_case().p_name);
+ auto program = Program::load(sourceStream);
+
+ BOOST_TEST(program.codeSize() == CodeSize::codeSizeIncludingFunctions(program.ast()));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END()
+
+}
diff --git a/test/yulPhaser/Random.cpp b/test/yulPhaser/Random.cpp
new file mode 100644
index 000000000..c69b91055
--- /dev/null
+++ b/test/yulPhaser/Random.cpp
@@ -0,0 +1,95 @@
+/*
+ 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
+
+using namespace std;
+
+namespace solidity::phaser::test
+{
+
+BOOST_AUTO_TEST_SUITE(Phaser)
+BOOST_AUTO_TEST_SUITE(RandomTest)
+
+BOOST_AUTO_TEST_CASE(uniformRandomInt_returns_different_values_when_called_multiple_times)
+{
+ constexpr uint32_t numSamples = 1000;
+ constexpr uint32_t numOutcomes = 100;
+
+ vector samples1;
+ vector samples2;
+ for (uint32_t i = 0; i < numSamples; ++i)
+ {
+ samples1.push_back(uniformRandomInt(0, numOutcomes - 1));
+ samples2.push_back(uniformRandomInt(0, numOutcomes - 1));
+ }
+
+ vector counts1(numSamples, 0);
+ vector counts2(numSamples, 0);
+ for (uint32_t i = 0; i < numSamples; ++i)
+ {
+ ++counts1[samples1[i]];
+ ++counts2[samples2[i]];
+ }
+
+ // This test rules out not only the possibility that the two sequences are the same but also
+ // that they're just different permutations of the same values. The test is probabilistic so
+ // it's technically possible for it to fail even if generator is good but the probability is
+ // so low that it would happen on average once very 10^125 billion years if you repeated it
+ // every second. The chance is much lower than 1 in 1000^100 / 100!.
+ //
+ // This does not really guarantee that the generated numbers have the right distribution or
+ // or that they don't come in long, repeating sequences but the implementation is very simple
+ // (it just calls a generator from boost) so our goal here is just to make sure it's used
+ // properly and we're not getting something totally non-random, e.g. the same number every time.
+ BOOST_TEST(counts1 != counts2);
+}
+
+BOOST_AUTO_TEST_CASE(binomialRandomInt_returns_different_values_when_called_multiple_times)
+{
+ constexpr uint32_t numSamples = 1000;
+ constexpr uint32_t numTrials = 100;
+ constexpr double successProbability = 0.6;
+
+ vector samples1;
+ vector samples2;
+ for (uint32_t i = 0; i < numSamples; ++i)
+ {
+ samples1.push_back(binomialRandomInt(numTrials, successProbability));
+ samples2.push_back(binomialRandomInt(numTrials, successProbability));
+ }
+
+ vector counts1(numSamples, 0);
+ vector counts2(numSamples, 0);
+ for (uint32_t i = 0; i < numSamples; ++i)
+ {
+ ++counts1[samples1[i]];
+ ++counts2[samples2[i]];
+ }
+
+ // See remark for uniformRandomInt() above. Same applies here.
+ BOOST_TEST(counts1 != counts2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END()
+
+}
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index bdcf615af..a2aa807af 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -12,3 +12,18 @@ target_link_libraries(solidity-upgrade PRIVATE solidity Boost::boost Boost::prog
include(GNUInstallDirs)
install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}")
+
+add_executable(yul-phaser
+ yulPhaser/main.cpp
+ yulPhaser/Population.h
+ yulPhaser/Population.cpp
+ yulPhaser/Chromosome.h
+ yulPhaser/Chromosome.cpp
+ yulPhaser/Program.h
+ yulPhaser/Program.cpp
+ yulPhaser/Random.h
+ yulPhaser/Random.cpp
+)
+target_link_libraries(yul-phaser PRIVATE solidity Boost::program_options)
+
+install(TARGETS yul-phaser DESTINATION "${CMAKE_INSTALL_BINDIR}")
diff --git a/tools/yulPhaser/Chromosome.cpp b/tools/yulPhaser/Chromosome.cpp
new file mode 100644
index 000000000..b332266ba
--- /dev/null
+++ b/tools/yulPhaser/Chromosome.cpp
@@ -0,0 +1,70 @@
+/*
+ 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
+
+using namespace std;
+using namespace solidity;
+using namespace solidity::yul;
+using namespace solidity::phaser;
+
+namespace solidity::phaser
+{
+
+ostream& operator<<(ostream& _stream, Chromosome const& _chromosome);
+
+}
+
+Chromosome Chromosome::makeRandom(size_t _length)
+{
+ vector steps;
+ for (size_t i = 0; i < _length; ++i)
+ steps.push_back(randomOptimisationStep());
+
+ return Chromosome(move(steps));
+}
+
+ostream& phaser::operator<<(ostream& _stream, Chromosome const& _chromosome)
+{
+ for (auto const& stepName: _chromosome.m_optimisationSteps)
+ _stream << OptimiserSuite::stepNameToAbbreviationMap().at(stepName);
+
+ return _stream;
+}
+
+vector Chromosome::allStepNames()
+{
+ vector stepNames;
+ for (auto const& step: OptimiserSuite::allSteps())
+ stepNames.push_back(step.first);
+
+ return stepNames;
+}
+
+string const& Chromosome::randomOptimisationStep()
+{
+ static vector stepNames = allStepNames();
+
+ return stepNames[uniformRandomInt(0, stepNames.size() - 1)];
+}
diff --git a/tools/yulPhaser/Chromosome.h b/tools/yulPhaser/Chromosome.h
new file mode 100644
index 000000000..6db99dc4e
--- /dev/null
+++ b/tools/yulPhaser/Chromosome.h
@@ -0,0 +1,62 @@
+/*
+ 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 .
+*/
+
+#pragma once
+
+#include