Merge pull request #8453 from imapp-pl/configurable-code-size-metric

Configurable CodeSize metric
This commit is contained in:
chriseth 2020-05-20 17:15:41 +02:00 committed by GitHub
commit ed0f2d463f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 485 additions and 102 deletions

View File

@ -36,30 +36,71 @@ using namespace solidity;
using namespace solidity::yul; using namespace solidity::yul;
using namespace solidity::util; using namespace solidity::util;
size_t CodeSize::codeSize(Statement const& _statement) size_t CodeWeights::costOf(Statement const& _statement) const
{ {
CodeSize cs; if (holds_alternative<ExpressionStatement>(_statement))
return expressionStatementCost;
else if (holds_alternative<Assignment>(_statement))
return assignmentCost;
else if (holds_alternative<VariableDeclaration>(_statement))
return variableDeclarationCost;
else if (holds_alternative<FunctionDefinition>(_statement))
return functionDefinitionCost;
else if (holds_alternative<If>(_statement))
return ifCost;
else if (holds_alternative<Switch>(_statement))
return switchCost + caseCost * std::get<Switch>(_statement).cases.size();
else if (holds_alternative<ForLoop>(_statement))
return forLoopCost;
else if (holds_alternative<Break>(_statement))
return breakCost;
else if (holds_alternative<Continue>(_statement))
return continueCost;
else if (holds_alternative<Leave>(_statement))
return leaveCost;
else if (holds_alternative<Block>(_statement))
return blockCost;
else
yulAssert(false, "If you add a new statement type, you must update CodeWeights.");
}
size_t CodeWeights::costOf(Expression const& _expression) const
{
if (holds_alternative<FunctionCall>(_expression))
return functionCallCost;
else if (holds_alternative<Identifier>(_expression))
return identifierCost;
else if (holds_alternative<Literal>(_expression))
return literalCost;
else
yulAssert(false, "If you add a new expression type, you must update CodeWeights.");
}
size_t CodeSize::codeSize(Statement const& _statement, CodeWeights const& _weights)
{
CodeSize cs(true, _weights);
cs.visit(_statement); cs.visit(_statement);
return cs.m_size; return cs.m_size;
} }
size_t CodeSize::codeSize(Expression const& _expression) size_t CodeSize::codeSize(Expression const& _expression, CodeWeights const& _weights)
{ {
CodeSize cs; CodeSize cs(true, _weights);
cs.visit(_expression); cs.visit(_expression);
return cs.m_size; return cs.m_size;
} }
size_t CodeSize::codeSize(Block const& _block) size_t CodeSize::codeSize(Block const& _block, CodeWeights const& _weights)
{ {
CodeSize cs; CodeSize cs(true, _weights);
cs(_block); cs(_block);
return cs.m_size; return cs.m_size;
} }
size_t CodeSize::codeSizeIncludingFunctions(Block const& _block) size_t CodeSize::codeSizeIncludingFunctions(Block const& _block, CodeWeights const& _weights)
{ {
CodeSize cs(false); CodeSize cs(false, _weights);
cs(_block); cs(_block);
return cs.m_size; return cs.m_size;
} }
@ -68,32 +109,14 @@ void CodeSize::visit(Statement const& _statement)
{ {
if (holds_alternative<FunctionDefinition>(_statement) && m_ignoreFunctions) if (holds_alternative<FunctionDefinition>(_statement) && m_ignoreFunctions)
return; return;
else if (
holds_alternative<If>(_statement) ||
holds_alternative<Break>(_statement) ||
holds_alternative<Continue>(_statement) ||
holds_alternative<Leave>(_statement)
)
m_size += 2;
else if (holds_alternative<ForLoop>(_statement))
m_size += 3;
else if (holds_alternative<Switch>(_statement))
m_size += 1 + 2 * std::get<Switch>(_statement).cases.size();
else if (!(
holds_alternative<Block>(_statement) ||
holds_alternative<ExpressionStatement>(_statement) ||
holds_alternative<Assignment>(_statement) ||
holds_alternative<VariableDeclaration>(_statement)
))
++m_size;
m_size += m_weights.costOf(_statement);
ASTWalker::visit(_statement); ASTWalker::visit(_statement);
} }
void CodeSize::visit(Expression const& _expression) void CodeSize::visit(Expression const& _expression)
{ {
if (!holds_alternative<Identifier>(_expression)) m_size += m_weights.costOf(_expression);
++m_size;
ASTWalker::visit(_expression); ASTWalker::visit(_expression);
} }

View File

@ -30,33 +30,67 @@ struct Dialect;
struct EVMDialect; struct EVMDialect;
/** /**
* Metric for the size of code. * Weights to be assigned to specific yul statements and expressions by a metric.
* More specifically, the number of AST nodes.
* Ignores function definitions while traversing the AST by default.
* If you want to know the size of a function, you have to invoke this on its body.
* *
* As an exception, the following AST elements have a cost of zero: * The default values are meant to reflect specifically the number of AST nodes.
*
* The following AST elements have a default cost of zero (because the cleanup phase would
* remove them anyway or they are just wrappers around something else will be counted instead):
* - expression statement (only the expression inside has a cost) * - expression statement (only the expression inside has a cost)
* - block (only the statements inside have a cost) * - block (only the statements inside have a cost)
* - variable references * - variable references
* - variable declarations (only the right hand side has a cost) * - variable declarations (only the right hand side has a cost)
* - assignments (only the value has a cost) * - assignments (only the value has a cost)
* *
* As another exception, each statement incurs and additional cost of one * Each statement incurs and additional cost of one
* per jump/branch. This means if, break and continue statements have a cost of 2, * per jump/branch. This means if, break and continue statements have a cost of 2,
* switch statements have a cost of 1 plus the number of cases times two, * switch statements have a cost of 1 plus the number of cases times two,
* and for loops cost 3. * and for loops cost 3.
*/
struct CodeWeights
{
// Statements
size_t expressionStatementCost = 0;
size_t assignmentCost = 0;
size_t variableDeclarationCost = 0;
size_t functionDefinitionCost = 1;
size_t ifCost = 2;
size_t switchCost = 1;
size_t caseCost = 2;
size_t forLoopCost = 3;
size_t breakCost = 2;
size_t continueCost = 2;
size_t leaveCost = 2;
size_t blockCost = 0;
// Expressions
size_t functionCallCost = 1;
size_t identifierCost = 0;
size_t literalCost = 1;
size_t costOf(Statement const& _statement) const;
size_t costOf(Expression const& _expression) const;
};
/**
* Metric for the size of code.
* Ignores function definitions while traversing the AST by default.
* If you want to know the size of a function, you have to invoke this on its body.
*
* The cost of each statement and expression type is configurable via CodeWeights.
*/ */
class CodeSize: public ASTWalker class CodeSize: public ASTWalker
{ {
public: public:
static size_t codeSize(Statement const& _statement); static size_t codeSize(Statement const& _statement, CodeWeights const& _weights = {});
static size_t codeSize(Expression const& _expression); static size_t codeSize(Expression const& _expression, CodeWeights const& _weights = {});
static size_t codeSize(Block const& _block); static size_t codeSize(Block const& _block, CodeWeights const& _weights = {});
static size_t codeSizeIncludingFunctions(Block const& _block); static size_t codeSizeIncludingFunctions(Block const& _block, CodeWeights const& _weights = {});
private: private:
CodeSize(bool _ignoreFunctions = true): m_ignoreFunctions(_ignoreFunctions) {} CodeSize(bool _ignoreFunctions = true, CodeWeights const& _weights = {}):
m_ignoreFunctions(_ignoreFunctions),
m_weights(_weights) {}
void visit(Statement const& _statement) override; void visit(Statement const& _statement) override;
void visit(Expression const& _expression) override; void visit(Expression const& _expression) override;
@ -64,6 +98,7 @@ private:
private: private:
bool m_ignoreFunctions; bool m_ignoreFunctions;
size_t m_size = 0; size_t m_size = 0;
CodeWeights m_weights;
}; };
/** /**

View File

@ -36,15 +36,38 @@ namespace solidity::yul::test
namespace namespace
{ {
size_t codeSize(string const& _source) size_t codeSize(string const& _source, CodeWeights const _weights = {})
{ {
shared_ptr<Block> ast = parse(_source, false).first; shared_ptr<Block> ast = parse(_source, false).first;
BOOST_REQUIRE(ast); BOOST_REQUIRE(ast);
return CodeSize::codeSize(*ast); return CodeSize::codeSize(*ast, _weights);
} }
} }
class CustomWeightFixture
{
protected:
CodeWeights m_weights{
/* expressionStatementCost = */ 1,
/* assignmentCost = */ 2,
/* variableDeclarationCost = */ 3,
/* functionDefinitionCost = */ 4,
/* ifCost = */ 5,
/* switchCost = */ 6,
/* caseCost = */ 7,
/* forLoopCost = */ 8,
/* breakCost = */ 9,
/* continueCost = */ 10,
/* leaveCost = */ 11,
/* blockCost = */ 12,
/* functionCallCost = */ 13,
/* identifierCost = */ 14,
/* literalCost = */ 15,
};
};
BOOST_AUTO_TEST_SUITE(YulCodeSize) BOOST_AUTO_TEST_SUITE(YulCodeSize)
BOOST_AUTO_TEST_CASE(empty_code) BOOST_AUTO_TEST_CASE(empty_code)
@ -52,41 +75,103 @@ BOOST_AUTO_TEST_CASE(empty_code)
BOOST_CHECK_EQUAL(codeSize("{}"), 0); BOOST_CHECK_EQUAL(codeSize("{}"), 0);
} }
BOOST_FIXTURE_TEST_CASE(empty_code_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(codeSize("{}", m_weights), 0);
}
BOOST_AUTO_TEST_CASE(nested_blocks) BOOST_AUTO_TEST_CASE(nested_blocks)
{ {
BOOST_CHECK_EQUAL(codeSize("{ {} {} {{ }} }"), 0); BOOST_CHECK_EQUAL(codeSize("{ {} {} {{ }} }"), 0);
} }
BOOST_FIXTURE_TEST_CASE(nested_blocks_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(codeSize("{ {} {} {{ }} }", m_weights), 4 * m_weights.blockCost);
}
BOOST_AUTO_TEST_CASE(instruction) BOOST_AUTO_TEST_CASE(instruction)
{ {
BOOST_CHECK_EQUAL(codeSize("{ pop(calldatasize()) }"), 2); BOOST_CHECK_EQUAL(codeSize("{ pop(calldatasize()) }"), 2);
} }
BOOST_FIXTURE_TEST_CASE(instruction_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(
codeSize("{ pop(calldatasize()) }", m_weights),
2 * m_weights.functionCallCost +
1 * m_weights.expressionStatementCost
);
}
BOOST_AUTO_TEST_CASE(variables_are_free) BOOST_AUTO_TEST_CASE(variables_are_free)
{ {
BOOST_CHECK_EQUAL(codeSize("{ let x let y let a, b, c }"), 0); BOOST_CHECK_EQUAL(codeSize("{ let x let y let a, b, c }"), 0);
} }
BOOST_FIXTURE_TEST_CASE(variables_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(
codeSize("{ let x let y let a, b, c }", m_weights),
3 * m_weights.variableDeclarationCost
);
}
BOOST_AUTO_TEST_CASE(constants_cost_one) BOOST_AUTO_TEST_CASE(constants_cost_one)
{ {
BOOST_CHECK_EQUAL(codeSize("{ let x := 3 }"), 1); BOOST_CHECK_EQUAL(codeSize("{ let x := 3 }"), 1);
} }
BOOST_FIXTURE_TEST_CASE(constants_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(
codeSize("{ let x := 3 }", m_weights),
1 * m_weights.variableDeclarationCost +
1 * m_weights.literalCost
);
}
BOOST_AUTO_TEST_CASE(functions_are_skipped) BOOST_AUTO_TEST_CASE(functions_are_skipped)
{ {
BOOST_CHECK_EQUAL(codeSize("{ function f(x) -> r { r := mload(x) } }"), 0); BOOST_CHECK_EQUAL(codeSize("{ function f(x) -> r { r := mload(x) } }"), 0);
} }
BOOST_FIXTURE_TEST_CASE(functions_are_skipped_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(codeSize("{ function f(x) -> r { r := mload(x) } }", m_weights), 0);
}
BOOST_AUTO_TEST_CASE(function_with_arguments) BOOST_AUTO_TEST_CASE(function_with_arguments)
{ {
BOOST_CHECK_EQUAL(codeSize("{ function f(x) { sstore(x, 2) } f(2) }"), 2); BOOST_CHECK_EQUAL(codeSize("{ function f(x) { sstore(x, 2) } f(2) }"), 2);
} }
BOOST_FIXTURE_TEST_CASE(function_with_arguments_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(
codeSize("{ function f(x) { sstore(x, 2) } f(2) }", m_weights),
1 * m_weights.expressionStatementCost +
1 * m_weights.functionCallCost +
1 * m_weights.literalCost
);
}
BOOST_AUTO_TEST_CASE(function_with_variables_as_arguments) BOOST_AUTO_TEST_CASE(function_with_variables_as_arguments)
{ {
BOOST_CHECK_EQUAL(codeSize("{ function f(x) { sstore(x, 2) } let y f(y) }"), 1); BOOST_CHECK_EQUAL(codeSize("{ function f(x) { sstore(x, 2) } let y f(y) }"), 1);
} }
BOOST_FIXTURE_TEST_CASE(function_with_variables_as_arguments_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(
codeSize("{ function f(x) { sstore(x, 2) } let y f(y) }", m_weights),
1 * m_weights.variableDeclarationCost +
1 * m_weights.expressionStatementCost +
1 * m_weights.functionCallCost +
1 * m_weights.identifierCost
);
}
BOOST_AUTO_TEST_CASE(function_with_variables_and_constants_as_arguments) BOOST_AUTO_TEST_CASE(function_with_variables_and_constants_as_arguments)
{ {
BOOST_CHECK_EQUAL(codeSize( BOOST_CHECK_EQUAL(codeSize(
@ -94,21 +179,69 @@ BOOST_AUTO_TEST_CASE(function_with_variables_and_constants_as_arguments)
), 2); ), 2);
} }
BOOST_FIXTURE_TEST_CASE(
function_with_variables_and_constants_as_arguments_custom_weights,
CustomWeightFixture
)
{
BOOST_CHECK_EQUAL(
codeSize(
"{ function f(x, r) -> z { sstore(x, r) z := r } let y let t := f(y, 2) }",
m_weights
),
2 * m_weights.variableDeclarationCost +
1 * m_weights.functionCallCost +
1 * m_weights.identifierCost +
1 * m_weights.literalCost
);
}
BOOST_AUTO_TEST_CASE(assignment) BOOST_AUTO_TEST_CASE(assignment)
{ {
BOOST_CHECK_EQUAL(codeSize("{ let a a := 3 }"), 1); BOOST_CHECK_EQUAL(codeSize("{ let a a := 3 }"), 1);
} }
BOOST_FIXTURE_TEST_CASE(assignment_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(
codeSize("{ let a a := 3 }", m_weights),
1 * m_weights.variableDeclarationCost +
1 * m_weights.assignmentCost +
1 * m_weights.literalCost
);
}
BOOST_AUTO_TEST_CASE(assignments_between_vars_are_free) BOOST_AUTO_TEST_CASE(assignments_between_vars_are_free)
{ {
BOOST_CHECK_EQUAL(codeSize("{ let a let b := a a := b }"), 0); BOOST_CHECK_EQUAL(codeSize("{ let a let b := a a := b }"), 0);
} }
BOOST_FIXTURE_TEST_CASE(assignments_between_vars_are_free_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(
codeSize("{ let a let b := a a := b }", m_weights),
2 * m_weights.variableDeclarationCost +
1 * m_weights.assignmentCost +
2 * m_weights.identifierCost
);
}
BOOST_AUTO_TEST_CASE(assignment_complex) BOOST_AUTO_TEST_CASE(assignment_complex)
{ {
BOOST_CHECK_EQUAL(codeSize("{ let a let x := mload(a) a := sload(x) }"), 2); BOOST_CHECK_EQUAL(codeSize("{ let a let x := mload(a) a := sload(x) }"), 2);
} }
BOOST_FIXTURE_TEST_CASE(assignment_complex_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(
codeSize("{ let a let x := mload(a) a := sload(x) }", m_weights),
2 * m_weights.variableDeclarationCost +
1 * m_weights.assignmentCost +
2 * m_weights.identifierCost +
2 * m_weights.functionCallCost
);
}
BOOST_AUTO_TEST_CASE(empty_for_loop) BOOST_AUTO_TEST_CASE(empty_for_loop)
{ {
BOOST_CHECK_EQUAL(codeSize( BOOST_CHECK_EQUAL(codeSize(
@ -116,6 +249,15 @@ BOOST_AUTO_TEST_CASE(empty_for_loop)
), 4); ), 4);
} }
BOOST_FIXTURE_TEST_CASE(empty_for_loop_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(
codeSize("{ for {} 1 {} {} }", m_weights),
1 * m_weights.forLoopCost +
1 * m_weights.literalCost
);
}
BOOST_AUTO_TEST_CASE(break_statement) BOOST_AUTO_TEST_CASE(break_statement)
{ {
BOOST_CHECK_EQUAL(codeSize( BOOST_CHECK_EQUAL(codeSize(
@ -123,6 +265,16 @@ BOOST_AUTO_TEST_CASE(break_statement)
), 6); ), 6);
} }
BOOST_FIXTURE_TEST_CASE(break_statement_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(
codeSize("{ for {} 1 {} { break } }", m_weights),
1 * m_weights.forLoopCost +
1 * m_weights.literalCost +
1 * m_weights.breakCost
);
}
BOOST_AUTO_TEST_CASE(continue_statement) BOOST_AUTO_TEST_CASE(continue_statement)
{ {
BOOST_CHECK_EQUAL(codeSize( BOOST_CHECK_EQUAL(codeSize(
@ -130,6 +282,16 @@ BOOST_AUTO_TEST_CASE(continue_statement)
), 6); ), 6);
} }
BOOST_FIXTURE_TEST_CASE(continue_statement_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(
codeSize("{ for {} 1 {} { continue } }", m_weights),
1 * m_weights.forLoopCost +
1 * m_weights.literalCost +
1 * m_weights.continueCost
);
}
BOOST_AUTO_TEST_CASE(regular_for_loop) BOOST_AUTO_TEST_CASE(regular_for_loop)
{ {
BOOST_CHECK_EQUAL(codeSize( BOOST_CHECK_EQUAL(codeSize(
@ -137,6 +299,20 @@ BOOST_AUTO_TEST_CASE(regular_for_loop)
), 10); ), 10);
} }
BOOST_FIXTURE_TEST_CASE(regular_for_loop_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(
codeSize("{ for { let x := 0 } lt(x, 10) { x := add(x, 1) } { mstore(x, 1) } }", m_weights),
1 * m_weights.forLoopCost +
1 * m_weights.variableDeclarationCost +
1 * m_weights.assignmentCost +
3 * m_weights.functionCallCost +
4 * m_weights.literalCost +
3 * m_weights.identifierCost +
1 * m_weights.expressionStatementCost
);
}
BOOST_AUTO_TEST_CASE(if_statement) BOOST_AUTO_TEST_CASE(if_statement)
{ {
BOOST_CHECK_EQUAL(codeSize( BOOST_CHECK_EQUAL(codeSize(
@ -144,6 +320,15 @@ BOOST_AUTO_TEST_CASE(if_statement)
), 3); ), 3);
} }
BOOST_FIXTURE_TEST_CASE(if_statement_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(
codeSize("{ if 1 {} }", m_weights),
1 * m_weights.ifCost +
1 * m_weights.literalCost
);
}
BOOST_AUTO_TEST_CASE(switch_statement_tiny) BOOST_AUTO_TEST_CASE(switch_statement_tiny)
{ {
BOOST_CHECK_EQUAL(codeSize( BOOST_CHECK_EQUAL(codeSize(
@ -158,6 +343,16 @@ BOOST_AUTO_TEST_CASE(switch_statement_small)
), 6); ), 6);
} }
BOOST_FIXTURE_TEST_CASE(switch_statement_small_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(
codeSize("{ switch calldatasize() case 0 {} default {} }", m_weights),
1 * m_weights.functionCallCost +
1 * m_weights.switchCost +
2 * m_weights.caseCost
);
}
BOOST_AUTO_TEST_CASE(switch_statement_medium) BOOST_AUTO_TEST_CASE(switch_statement_medium)
{ {
BOOST_CHECK_EQUAL(codeSize( BOOST_CHECK_EQUAL(codeSize(
@ -172,6 +367,16 @@ BOOST_AUTO_TEST_CASE(switch_statement_large)
), 10); ), 10);
} }
BOOST_FIXTURE_TEST_CASE(switch_statement_large_custom_weights, CustomWeightFixture)
{
BOOST_CHECK_EQUAL(
codeSize("{ switch calldatasize() case 0 {} case 1 {} case 2 {} default {} }", m_weights),
1 * m_weights.functionCallCost +
1 * m_weights.switchCost +
4 * m_weights.caseCost
);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

View File

@ -37,6 +37,7 @@ using namespace boost::unit_test::framework;
using namespace boost::test_tools; using namespace boost::test_tools;
using namespace solidity::langutil; using namespace solidity::langutil;
using namespace solidity::util; using namespace solidity::util;
using namespace solidity::yul;
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
@ -299,8 +300,8 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_cache_stats_if_requested, AlgorithmRunn
make_shared<ProgramCache>(programs[1]), make_shared<ProgramCache>(programs[1]),
}; };
shared_ptr<FitnessMetric> fitnessMetric = make_shared<FitnessMetricAverage>(vector<shared_ptr<FitnessMetric>>{ shared_ptr<FitnessMetric> fitnessMetric = make_shared<FitnessMetricAverage>(vector<shared_ptr<FitnessMetric>>{
make_shared<ProgramSize>(nullopt, caches[0]), make_shared<ProgramSize>(nullopt, caches[0], CodeWeights{}),
make_shared<ProgramSize>(nullopt, caches[1]), make_shared<ProgramSize>(nullopt, caches[1], CodeWeights{}),
}); });
Population population = Population::makeRandom(fitnessMetric, 2, 0, 5); Population population = Population::makeRandom(fitnessMetric, 2, 0, 5);

View File

@ -62,12 +62,12 @@ protected:
Program optimisedProgram(Program _program) const Program optimisedProgram(Program _program) const
{ {
[[maybe_unused]] size_t originalSize = _program.codeSize(); [[maybe_unused]] size_t originalSize = _program.codeSize(m_weights);
Program result = move(_program); Program result = move(_program);
result.optimise(m_chromosome.optimisationSteps()); result.optimise(m_chromosome.optimisationSteps());
// Make sure that the program and the chromosome we have chosen are suitable for the test // Make sure that the program and the chromosome we have chosen are suitable for the test
assert(result.codeSize() != originalSize); assert(result.codeSize(m_weights) != originalSize);
return result; return result;
} }
@ -77,15 +77,16 @@ protected:
Program m_program = get<Program>(Program::load(m_sourceStream)); Program m_program = get<Program>(Program::load(m_sourceStream));
Program m_optimisedProgram = optimisedProgram(m_program); Program m_optimisedProgram = optimisedProgram(m_program);
shared_ptr<ProgramCache> m_programCache = make_shared<ProgramCache>(m_program); shared_ptr<ProgramCache> m_programCache = make_shared<ProgramCache>(m_program);
static constexpr CodeWeights m_weights{};
}; };
class FitnessMetricCombinationFixture: public ProgramBasedMetricFixture class FitnessMetricCombinationFixture: public ProgramBasedMetricFixture
{ {
protected: protected:
vector<shared_ptr<FitnessMetric>> m_simpleMetrics = { vector<shared_ptr<FitnessMetric>> m_simpleMetrics = {
make_shared<ProgramSize>(m_program, nullptr, 1), make_shared<ProgramSize>(m_program, nullptr, m_weights, 1),
make_shared<ProgramSize>(m_program, nullptr, 2), make_shared<ProgramSize>(m_program, nullptr, m_weights, 2),
make_shared<ProgramSize>(m_program, nullptr, 3), make_shared<ProgramSize>(m_program, nullptr, m_weights, 3),
}; };
vector<size_t> m_fitness = { vector<size_t> m_fitness = {
m_simpleMetrics[0]->evaluate(m_chromosome), m_simpleMetrics[0]->evaluate(m_chromosome),
@ -100,7 +101,7 @@ BOOST_AUTO_TEST_SUITE(ProgramBasedMetricTest)
BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture) BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture)
{ {
string code = toString(DummyProgramBasedMetric(m_program, nullptr).optimisedProgram(m_chromosome)); string code = toString(DummyProgramBasedMetric(m_program, nullptr, m_weights).optimisedProgram(m_chromosome));
BOOST_TEST(code != toString(m_program)); BOOST_TEST(code != toString(m_program));
BOOST_TEST(code == toString(m_optimisedProgram)); BOOST_TEST(code == toString(m_optimisedProgram));
@ -108,7 +109,7 @@ BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_return_optimised_program_even_if
BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_use_cache_if_available, ProgramBasedMetricFixture) BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_use_cache_if_available, ProgramBasedMetricFixture)
{ {
string code = toString(DummyProgramBasedMetric(nullopt, m_programCache).optimisedProgram(m_chromosome)); string code = toString(DummyProgramBasedMetric(nullopt, m_programCache, m_weights).optimisedProgram(m_chromosome));
BOOST_TEST(code != toString(m_program)); BOOST_TEST(code != toString(m_program));
BOOST_TEST(code == toString(m_optimisedProgram)); BOOST_TEST(code == toString(m_optimisedProgram));
@ -117,7 +118,7 @@ BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_use_cache_if_available, ProgramB
BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture) BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture)
{ {
string code = toString(DummyProgramBasedMetric(m_program, nullptr).optimisedProgramNoCache(m_chromosome)); string code = toString(DummyProgramBasedMetric(m_program, nullptr, m_weights).optimisedProgramNoCache(m_chromosome));
BOOST_TEST(code != toString(m_program)); BOOST_TEST(code != toString(m_program));
BOOST_TEST(code == toString(m_optimisedProgram)); BOOST_TEST(code == toString(m_optimisedProgram));
@ -125,7 +126,7 @@ BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_return_optimised_program_
BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_not_use_cache_even_if_available, ProgramBasedMetricFixture) BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_not_use_cache_even_if_available, ProgramBasedMetricFixture)
{ {
string code = toString(DummyProgramBasedMetric(nullopt, m_programCache).optimisedProgramNoCache(m_chromosome)); string code = toString(DummyProgramBasedMetric(nullopt, m_programCache, m_weights).optimisedProgramNoCache(m_chromosome));
BOOST_TEST(code != toString(m_program)); BOOST_TEST(code != toString(m_program));
BOOST_TEST(code == toString(m_optimisedProgram)); BOOST_TEST(code == toString(m_optimisedProgram));
@ -137,18 +138,18 @@ BOOST_AUTO_TEST_SUITE(ProgramSizeTest)
BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_size_of_the_optimised_program, ProgramBasedMetricFixture) BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_size_of_the_optimised_program, ProgramBasedMetricFixture)
{ {
size_t fitness = ProgramSize(m_program, nullptr).evaluate(m_chromosome); size_t fitness = ProgramSize(m_program, nullptr, m_weights).evaluate(m_chromosome);
BOOST_TEST(fitness != m_program.codeSize()); BOOST_TEST(fitness != m_program.codeSize(m_weights));
BOOST_TEST(fitness == m_optimisedProgram.codeSize()); BOOST_TEST(fitness == m_optimisedProgram.codeSize(m_weights));
} }
BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture) BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture)
{ {
size_t fitness = ProgramSize(nullopt, m_programCache).evaluate(m_chromosome); size_t fitness = ProgramSize(nullopt, m_programCache, m_weights).evaluate(m_chromosome);
BOOST_TEST(fitness != m_program.codeSize()); BOOST_TEST(fitness != m_program.codeSize(m_weights));
BOOST_TEST(fitness == m_optimisedProgram.codeSize()); BOOST_TEST(fitness == m_optimisedProgram.codeSize(m_weights));
BOOST_TEST(m_programCache->size() == m_chromosome.length()); BOOST_TEST(m_programCache->size() == m_chromosome.length());
} }
@ -157,21 +158,21 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number
Program const& programOptimisedOnce = m_optimisedProgram; Program const& programOptimisedOnce = m_optimisedProgram;
Program programOptimisedTwice = optimisedProgram(programOptimisedOnce); Program programOptimisedTwice = optimisedProgram(programOptimisedOnce);
ProgramSize metric(m_program, nullptr, 2); ProgramSize metric(m_program, nullptr, m_weights, 2);
size_t fitness = metric.evaluate(m_chromosome); size_t fitness = metric.evaluate(m_chromosome);
BOOST_TEST(fitness != m_program.codeSize()); BOOST_TEST(fitness != m_program.codeSize(m_weights));
BOOST_TEST(fitness != programOptimisedOnce.codeSize()); BOOST_TEST(fitness != programOptimisedOnce.codeSize(m_weights));
BOOST_TEST(fitness == programOptimisedTwice.codeSize()); BOOST_TEST(fitness == programOptimisedTwice.codeSize(m_weights));
} }
BOOST_FIXTURE_TEST_CASE(evaluate_should_not_optimise_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture) BOOST_FIXTURE_TEST_CASE(evaluate_should_not_optimise_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture)
{ {
ProgramSize metric(m_program, nullptr, 0); ProgramSize metric(m_program, nullptr, m_weights, 0);
size_t fitness = metric.evaluate(m_chromosome); size_t fitness = metric.evaluate(m_chromosome);
BOOST_TEST(fitness == m_program.codeSize()); BOOST_TEST(fitness == m_program.codeSize(m_weights));
BOOST_TEST(fitness != m_optimisedProgram.codeSize()); BOOST_TEST(fitness != m_optimisedProgram.codeSize(m_weights));
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
@ -179,12 +180,18 @@ BOOST_AUTO_TEST_SUITE(RelativeProgramSizeTest)
BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_the_size_ratio_between_optimised_program_and_original_program, ProgramBasedMetricFixture) BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_the_size_ratio_between_optimised_program_and_original_program, ProgramBasedMetricFixture)
{ {
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 3).evaluate(m_chromosome) == round(1000.0 * m_optimisedProgram.codeSize() / m_program.codeSize())); BOOST_TEST(
RelativeProgramSize(m_program, nullptr, 3, m_weights).evaluate(m_chromosome) ==
round(1000.0 * m_optimisedProgram.codeSize(m_weights) / m_program.codeSize(m_weights))
);
} }
BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture) BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture)
{ {
BOOST_TEST(RelativeProgramSize(nullopt, m_programCache, 3).evaluate(m_chromosome) == round(1000.0 * m_optimisedProgram.codeSize() / m_program.codeSize())); BOOST_TEST(
RelativeProgramSize(nullopt, m_programCache, 3, m_weights).evaluate(m_chromosome) ==
round(1000.0 * m_optimisedProgram.codeSize(m_weights) / m_program.codeSize(m_weights))
);
BOOST_TEST(m_programCache->size() == m_chromosome.length()); BOOST_TEST(m_programCache->size() == m_chromosome.length());
} }
@ -193,17 +200,17 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number
Program const& programOptimisedOnce = m_optimisedProgram; Program const& programOptimisedOnce = m_optimisedProgram;
Program programOptimisedTwice = optimisedProgram(programOptimisedOnce); Program programOptimisedTwice = optimisedProgram(programOptimisedOnce);
RelativeProgramSize metric(m_program, nullptr, 3, 2); RelativeProgramSize metric(m_program, nullptr, 3, m_weights, 2);
size_t fitness = metric.evaluate(m_chromosome); size_t fitness = metric.evaluate(m_chromosome);
BOOST_TEST(fitness != 1000); BOOST_TEST(fitness != 1000);
BOOST_TEST(fitness != RelativeProgramSize(programOptimisedTwice, nullptr, 3, 1).evaluate(m_chromosome)); BOOST_TEST(fitness != RelativeProgramSize(programOptimisedTwice, nullptr, 3, m_weights, 1).evaluate(m_chromosome));
BOOST_TEST(fitness == round(1000.0 * programOptimisedTwice.codeSize() / m_program.codeSize())); BOOST_TEST(fitness == round(1000.0 * programOptimisedTwice.codeSize(m_weights) / m_program.codeSize(m_weights)));
} }
BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture) BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture)
{ {
RelativeProgramSize metric(m_program, nullptr, 3, 0); RelativeProgramSize metric(m_program, nullptr, 3, m_weights, 0);
BOOST_TEST(metric.evaluate(m_chromosome) == 1000); BOOST_TEST(metric.evaluate(m_chromosome) == 1000);
} }
@ -213,7 +220,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_the_original_program_size_
CharStream sourceStream = CharStream("{}", ""); CharStream sourceStream = CharStream("{}", "");
Program program = get<Program>(Program::load(sourceStream)); Program program = get<Program>(Program::load(sourceStream));
RelativeProgramSize metric(program, nullptr, 3); RelativeProgramSize metric(program, nullptr, 3, m_weights);
BOOST_TEST(metric.evaluate(m_chromosome) == 1000); BOOST_TEST(metric.evaluate(m_chromosome) == 1000);
BOOST_TEST(metric.evaluate(Chromosome("")) == 1000); BOOST_TEST(metric.evaluate(Chromosome("")) == 1000);
@ -222,12 +229,12 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_the_original_program_size_
BOOST_FIXTURE_TEST_CASE(evaluate_should_multiply_the_result_by_scaling_factor, ProgramBasedMetricFixture) BOOST_FIXTURE_TEST_CASE(evaluate_should_multiply_the_result_by_scaling_factor, ProgramBasedMetricFixture)
{ {
double sizeRatio = static_cast<double>(m_optimisedProgram.codeSize()) / m_program.codeSize(); double sizeRatio = static_cast<double>(m_optimisedProgram.codeSize(m_weights)) / m_program.codeSize(m_weights);
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 0).evaluate(m_chromosome) == round(1.0 * sizeRatio)); BOOST_TEST(RelativeProgramSize(m_program, nullptr, 0, m_weights).evaluate(m_chromosome) == round(1.0 * sizeRatio));
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 1).evaluate(m_chromosome) == round(10.0 * sizeRatio)); BOOST_TEST(RelativeProgramSize(m_program, nullptr, 1, m_weights).evaluate(m_chromosome) == round(10.0 * sizeRatio));
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 2).evaluate(m_chromosome) == round(100.0 * sizeRatio)); BOOST_TEST(RelativeProgramSize(m_program, nullptr, 2, m_weights).evaluate(m_chromosome) == round(100.0 * sizeRatio));
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 3).evaluate(m_chromosome) == round(1000.0 * sizeRatio)); BOOST_TEST(RelativeProgramSize(m_program, nullptr, 3, m_weights).evaluate(m_chromosome) == round(1000.0 * sizeRatio));
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 4).evaluate(m_chromosome) == round(10000.0 * sizeRatio)); BOOST_TEST(RelativeProgramSize(m_program, nullptr, 4, m_weights).evaluate(m_chromosome) == round(10000.0 * sizeRatio));
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View File

@ -32,6 +32,7 @@
using namespace std; using namespace std;
using namespace solidity::util; using namespace solidity::util;
using namespace solidity::langutil; using namespace solidity::langutil;
using namespace solidity::yul;
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
@ -86,6 +87,7 @@ protected:
/* relativeMetricScale = */ 5, /* relativeMetricScale = */ 5,
/* chromosomeRepetitions = */ 1, /* chromosomeRepetitions = */ 1,
}; };
CodeWeights const m_weights{};
}; };
class PoulationFactoryFixture class PoulationFactoryFixture
@ -183,7 +185,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_create_metric_of_the_right_type, FitnessMet
{ {
m_options.metric = MetricChoice::RelativeCodeSize; m_options.metric = MetricChoice::RelativeCodeSize;
m_options.metricAggregator = MetricAggregatorChoice::Sum; m_options.metricAggregator = MetricAggregatorChoice::Sum;
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}); unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}, m_weights);
BOOST_REQUIRE(metric != nullptr); BOOST_REQUIRE(metric != nullptr);
auto sumMetric = dynamic_cast<FitnessMetricSum*>(metric.get()); auto sumMetric = dynamic_cast<FitnessMetricSum*>(metric.get());
@ -201,7 +203,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_respect_chromosome_repetitions_option, Fitn
m_options.metric = MetricChoice::CodeSize; m_options.metric = MetricChoice::CodeSize;
m_options.metricAggregator = MetricAggregatorChoice::Average; m_options.metricAggregator = MetricAggregatorChoice::Average;
m_options.chromosomeRepetitions = 5; m_options.chromosomeRepetitions = 5;
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}); unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}, m_weights);
BOOST_REQUIRE(metric != nullptr); BOOST_REQUIRE(metric != nullptr);
auto averageMetric = dynamic_cast<FitnessMetricAverage*>(metric.get()); auto averageMetric = dynamic_cast<FitnessMetricAverage*>(metric.get());
@ -219,7 +221,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_set_relative_metric_scale, FitnessMetricFac
m_options.metric = MetricChoice::RelativeCodeSize; m_options.metric = MetricChoice::RelativeCodeSize;
m_options.metricAggregator = MetricAggregatorChoice::Average; m_options.metricAggregator = MetricAggregatorChoice::Average;
m_options.relativeMetricScale = 10; m_options.relativeMetricScale = 10;
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}); unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}, m_weights);
BOOST_REQUIRE(metric != nullptr); BOOST_REQUIRE(metric != nullptr);
auto averageMetric = dynamic_cast<FitnessMetricAverage*>(metric.get()); auto averageMetric = dynamic_cast<FitnessMetricAverage*>(metric.get());
@ -237,7 +239,8 @@ BOOST_FIXTURE_TEST_CASE(build_should_create_metric_for_each_input_program, Fitne
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build( unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(
m_options, m_options,
m_programs, m_programs,
vector<shared_ptr<ProgramCache>>(m_programs.size(), nullptr) vector<shared_ptr<ProgramCache>>(m_programs.size(), nullptr),
m_weights
); );
BOOST_REQUIRE(metric != nullptr); BOOST_REQUIRE(metric != nullptr);
@ -256,7 +259,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_pass_program_caches_to_metrics, FitnessMetr
}; };
m_options.metric = MetricChoice::RelativeCodeSize; m_options.metric = MetricChoice::RelativeCodeSize;
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, m_programs, caches); unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, m_programs, caches, m_weights);
BOOST_REQUIRE(metric != nullptr); BOOST_REQUIRE(metric != nullptr);
auto combinedMetric = dynamic_cast<FitnessMetricCombination*>(metric.get()); auto combinedMetric = dynamic_cast<FitnessMetricCombination*>(metric.get());

View File

@ -398,7 +398,7 @@ BOOST_AUTO_TEST_CASE(codeSize)
CharStream sourceStream(sourceCode, current_test_case().p_name); CharStream sourceStream(sourceCode, current_test_case().p_name);
Program program = get<Program>(Program::load(sourceStream)); Program program = get<Program>(Program::load(sourceStream));
BOOST_TEST(program.codeSize() == CodeSize::codeSizeIncludingFunctions(program.ast())); BOOST_TEST(program.codeSize(CodeWeights{}) == CodeSize::codeSizeIncludingFunctions(program.ast()));
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View File

@ -18,6 +18,8 @@
#include <tools/yulPhaser/ProgramCache.h> #include <tools/yulPhaser/ProgramCache.h>
#include <tools/yulPhaser/Chromosome.h> #include <tools/yulPhaser/Chromosome.h>
#include <libyul/optimiser/Metrics.h>
#include <liblangutil/CharStream.h> #include <liblangutil/CharStream.h>
#include <libsolutil/CommonIO.h> #include <libsolutil/CommonIO.h>
@ -212,11 +214,11 @@ BOOST_FIXTURE_TEST_CASE(startRound_should_remove_entries_older_than_two_rounds,
BOOST_FIXTURE_TEST_CASE(gatherStats_should_return_cache_statistics, ProgramCacheFixture) BOOST_FIXTURE_TEST_CASE(gatherStats_should_return_cache_statistics, ProgramCacheFixture)
{ {
size_t sizeI = optimisedProgram(m_program, "I").codeSize(); size_t sizeI = optimisedProgram(m_program, "I").codeSize(CacheStats::StorageWeights);
size_t sizeIu = optimisedProgram(m_program, "Iu").codeSize(); size_t sizeIu = optimisedProgram(m_program, "Iu").codeSize(CacheStats::StorageWeights);
size_t sizeIuO = optimisedProgram(m_program, "IuO").codeSize(); size_t sizeIuO = optimisedProgram(m_program, "IuO").codeSize(CacheStats::StorageWeights);
size_t sizeL = optimisedProgram(m_program, "L").codeSize(); size_t sizeL = optimisedProgram(m_program, "L").codeSize(CacheStats::StorageWeights);
size_t sizeLT = optimisedProgram(m_program, "LT").codeSize(); size_t sizeLT = optimisedProgram(m_program, "LT").codeSize(CacheStats::StorageWeights);
m_programCache.optimiseProgram("L"); m_programCache.optimiseProgram("L");
m_programCache.optimiseProgram("Iu"); m_programCache.optimiseProgram("Iu");

View File

@ -23,6 +23,7 @@
using namespace std; using namespace std;
using namespace solidity::util; using namespace solidity::util;
using namespace solidity::yul;
using namespace solidity::phaser; using namespace solidity::phaser;
Program const& ProgramBasedMetric::program() const Program const& ProgramBasedMetric::program() const
@ -55,18 +56,18 @@ Program ProgramBasedMetric::optimisedProgramNoCache(Chromosome const& _chromosom
size_t ProgramSize::evaluate(Chromosome const& _chromosome) size_t ProgramSize::evaluate(Chromosome const& _chromosome)
{ {
return optimisedProgram(_chromosome).codeSize(); return optimisedProgram(_chromosome).codeSize(codeWeights());
} }
size_t RelativeProgramSize::evaluate(Chromosome const& _chromosome) size_t RelativeProgramSize::evaluate(Chromosome const& _chromosome)
{ {
size_t const scalingFactor = pow(10, m_fixedPointPrecision); size_t const scalingFactor = pow(10, m_fixedPointPrecision);
size_t unoptimisedSize = optimisedProgram(Chromosome("")).codeSize(); size_t unoptimisedSize = optimisedProgram(Chromosome("")).codeSize(codeWeights());
if (unoptimisedSize == 0) if (unoptimisedSize == 0)
return scalingFactor; return scalingFactor;
size_t optimisedSize = optimisedProgram(_chromosome).codeSize(); size_t optimisedSize = optimisedProgram(_chromosome).codeSize(codeWeights());
return static_cast<size_t>(round( return static_cast<size_t>(round(
static_cast<double>(optimisedSize) / unoptimisedSize * scalingFactor static_cast<double>(optimisedSize) / unoptimisedSize * scalingFactor

View File

@ -24,6 +24,8 @@
#include <tools/yulPhaser/Program.h> #include <tools/yulPhaser/Program.h>
#include <tools/yulPhaser/ProgramCache.h> #include <tools/yulPhaser/ProgramCache.h>
#include <libyul/optimiser/Metrics.h>
#include <cstddef> #include <cstddef>
#include <optional> #include <optional>
@ -64,10 +66,12 @@ public:
explicit ProgramBasedMetric( explicit ProgramBasedMetric(
std::optional<Program> _program, std::optional<Program> _program,
std::shared_ptr<ProgramCache> _programCache, std::shared_ptr<ProgramCache> _programCache,
yul::CodeWeights const& _codeWeights,
size_t _repetitionCount = 1 size_t _repetitionCount = 1
): ):
m_program(std::move(_program)), m_program(std::move(_program)),
m_programCache(std::move(_programCache)), m_programCache(std::move(_programCache)),
m_codeWeights(_codeWeights),
m_repetitionCount(_repetitionCount) m_repetitionCount(_repetitionCount)
{ {
assert(m_program.has_value() == (m_programCache == nullptr)); assert(m_program.has_value() == (m_programCache == nullptr));
@ -75,6 +79,7 @@ public:
Program const& program() const; Program const& program() const;
ProgramCache const* programCache() const { return m_programCache.get(); } ProgramCache const* programCache() const { return m_programCache.get(); }
yul::CodeWeights const& codeWeights() const { return m_codeWeights; }
size_t repetitionCount() const { return m_repetitionCount; } size_t repetitionCount() const { return m_repetitionCount; }
Program optimisedProgram(Chromosome const& _chromosome); Program optimisedProgram(Chromosome const& _chromosome);
@ -83,6 +88,7 @@ public:
private: private:
std::optional<Program> m_program; std::optional<Program> m_program;
std::shared_ptr<ProgramCache> m_programCache; std::shared_ptr<ProgramCache> m_programCache;
yul::CodeWeights m_codeWeights;
size_t m_repetitionCount; size_t m_repetitionCount;
}; };
@ -111,9 +117,10 @@ public:
std::optional<Program> _program, std::optional<Program> _program,
std::shared_ptr<ProgramCache> _programCache, std::shared_ptr<ProgramCache> _programCache,
size_t _fixedPointPrecision, size_t _fixedPointPrecision,
yul::CodeWeights const& _weights,
size_t _repetitionCount = 1 size_t _repetitionCount = 1
): ):
ProgramBasedMetric(std::move(_program), std::move(_programCache), _repetitionCount), ProgramBasedMetric(std::move(_program), std::move(_programCache), _weights, _repetitionCount),
m_fixedPointPrecision(_fixedPointPrecision) {} m_fixedPointPrecision(_fixedPointPrecision) {}
size_t fixedPointPrecision() const { return m_fixedPointPrecision; } size_t fixedPointPrecision() const { return m_fixedPointPrecision; }

View File

@ -39,6 +39,7 @@ using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::langutil; using namespace solidity::langutil;
using namespace solidity::util; using namespace solidity::util;
using namespace solidity::yul;
using namespace solidity::phaser; using namespace solidity::phaser;
namespace po = boost::program_options; namespace po = boost::program_options;
@ -188,6 +189,27 @@ unique_ptr<GeneticAlgorithm> GeneticAlgorithmFactory::build(
} }
} }
CodeWeights CodeWeightFactory::buildFromCommandLine(po::variables_map const& _arguments)
{
return {
_arguments["expression-statement-cost"].as<size_t>(),
_arguments["assignment-cost"].as<size_t>(),
_arguments["variable-declaration-cost"].as<size_t>(),
_arguments["function-definition-cost"].as<size_t>(),
_arguments["if-cost"].as<size_t>(),
_arguments["switch-cost"].as<size_t>(),
_arguments["case-cost"].as<size_t>(),
_arguments["for-loop-cost"].as<size_t>(),
_arguments["break-cost"].as<size_t>(),
_arguments["continue-cost"].as<size_t>(),
_arguments["leave-cost"].as<size_t>(),
_arguments["block-cost"].as<size_t>(),
_arguments["function-call-cost"].as<size_t>(),
_arguments["identifier-cost"].as<size_t>(),
_arguments["literal-cost"].as<size_t>(),
};
}
FitnessMetricFactory::Options FitnessMetricFactory::Options::fromCommandLine(po::variables_map const& _arguments) FitnessMetricFactory::Options FitnessMetricFactory::Options::fromCommandLine(po::variables_map const& _arguments)
{ {
return { return {
@ -201,7 +223,8 @@ FitnessMetricFactory::Options FitnessMetricFactory::Options::fromCommandLine(po:
unique_ptr<FitnessMetric> FitnessMetricFactory::build( unique_ptr<FitnessMetric> FitnessMetricFactory::build(
Options const& _options, Options const& _options,
vector<Program> _programs, vector<Program> _programs,
vector<shared_ptr<ProgramCache>> _programCaches vector<shared_ptr<ProgramCache>> _programCaches,
CodeWeights const& _weights
) )
{ {
assert(_programCaches.size() == _programs.size()); assert(_programCaches.size() == _programs.size());
@ -216,6 +239,7 @@ unique_ptr<FitnessMetric> FitnessMetricFactory::build(
metrics.push_back(make_unique<ProgramSize>( metrics.push_back(make_unique<ProgramSize>(
_programCaches[i] != nullptr ? optional<Program>{} : move(_programs[i]), _programCaches[i] != nullptr ? optional<Program>{} : move(_programs[i]),
move(_programCaches[i]), move(_programCaches[i]),
_weights,
_options.chromosomeRepetitions _options.chromosomeRepetitions
)); ));
@ -228,6 +252,7 @@ unique_ptr<FitnessMetric> FitnessMetricFactory::build(
_programCaches[i] != nullptr ? optional<Program>{} : move(_programs[i]), _programCaches[i] != nullptr ? optional<Program>{} : move(_programs[i]),
move(_programCaches[i]), move(_programCaches[i]),
_options.relativeMetricScale, _options.relativeMetricScale,
_weights,
_options.chromosomeRepetitions _options.chromosomeRepetitions
)); ));
break; break;
@ -653,6 +678,28 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription()
; ;
keywordDescription.add(metricsDescription); keywordDescription.add(metricsDescription);
po::options_description metricWeightDescription("METRIC WEIGHTS", lineLength, minDescriptionLength);
metricWeightDescription.add_options()
// TODO: We need to figure out the best set of weights for the phaser.
// This one is just a stopgap to make sure no statement or expression has zero cost.
("expression-statement-cost", po::value<size_t>()->value_name("<COST>")->default_value(1))
("assignment-cost", po::value<size_t>()->value_name("<COST>")->default_value(1))
("variable-declaration-cost", po::value<size_t>()->value_name("<COST>")->default_value(1))
("function-definition-cost", po::value<size_t>()->value_name("<COST>")->default_value(1))
("if-cost", po::value<size_t>()->value_name("<COST>")->default_value(2))
("switch-cost", po::value<size_t>()->value_name("<COST>")->default_value(1))
("case-cost", po::value<size_t>()->value_name("<COST>")->default_value(2))
("for-loop-cost", po::value<size_t>()->value_name("<COST>")->default_value(3))
("break-cost", po::value<size_t>()->value_name("<COST>")->default_value(2))
("continue-cost", po::value<size_t>()->value_name("<COST>")->default_value(2))
("leave-cost", po::value<size_t>()->value_name("<COST>")->default_value(2))
("block-cost", po::value<size_t>()->value_name("<COST>")->default_value(1))
("function-call-cost", po::value<size_t>()->value_name("<COST>")->default_value(1))
("identifier-cost", po::value<size_t>()->value_name("<COST>")->default_value(1))
("literal-cost", po::value<size_t>()->value_name("<COST>")->default_value(1))
;
keywordDescription.add(metricWeightDescription);
po::options_description cacheDescription("CACHE", lineLength, minDescriptionLength); po::options_description cacheDescription("CACHE", lineLength, minDescriptionLength);
cacheDescription.add_options() cacheDescription.add_options()
( (
@ -762,8 +809,13 @@ void Phaser::runPhaser(po::variables_map const& _arguments)
vector<Program> programs = ProgramFactory::build(programOptions); vector<Program> programs = ProgramFactory::build(programOptions);
vector<shared_ptr<ProgramCache>> programCaches = ProgramCacheFactory::build(cacheOptions, programs); vector<shared_ptr<ProgramCache>> programCaches = ProgramCacheFactory::build(cacheOptions, programs);
CodeWeights codeWeights = CodeWeightFactory::buildFromCommandLine(_arguments);
unique_ptr<FitnessMetric> fitnessMetric = FitnessMetricFactory::build(metricOptions, programs, programCaches); unique_ptr<FitnessMetric> fitnessMetric = FitnessMetricFactory::build(
metricOptions,
programs,
programCaches,
codeWeights
);
Population population = PopulationFactory::build(populationOptions, move(fitnessMetric)); Population population = PopulationFactory::build(populationOptions, move(fitnessMetric));
if (_arguments["mode"].as<PhaserMode>() == PhaserMode::RunAlgorithm) if (_arguments["mode"].as<PhaserMode>() == PhaserMode::RunAlgorithm)

View File

@ -39,6 +39,13 @@ class CharStream;
} }
namespace solidity::yul
{
struct CodeWeights;
}
namespace solidity::phaser namespace solidity::phaser
{ {
@ -125,6 +132,17 @@ public:
); );
}; };
/**
* Builds and validates instances of @a CodeWeights.
*/
class CodeWeightFactory
{
public:
static yul::CodeWeights buildFromCommandLine(
boost::program_options::variables_map const& _arguments
);
};
/** /**
* Builds and validates instances of @a FitnessMetric and its derived classes. * Builds and validates instances of @a FitnessMetric and its derived classes.
*/ */
@ -144,7 +162,8 @@ public:
static std::unique_ptr<FitnessMetric> build( static std::unique_ptr<FitnessMetric> build(
Options const& _options, Options const& _options,
std::vector<Program> _programs, std::vector<Program> _programs,
std::vector<std::shared_ptr<ProgramCache>> _programCaches std::vector<std::shared_ptr<ProgramCache>> _programCaches,
yul::CodeWeights const& _weights
); );
}; };

View File

@ -207,7 +207,7 @@ unique_ptr<Block> Program::applyOptimisationSteps(
return _ast; return _ast;
} }
size_t Program::computeCodeSize(Block const& _ast) size_t Program::computeCodeSize(Block const& _ast, CodeWeights const& _weights)
{ {
return CodeSize::codeSizeIncludingFunctions(_ast); return CodeSize::codeSizeIncludingFunctions(_ast, _weights);
} }

View File

@ -41,6 +41,7 @@ namespace solidity::yul
struct AsmAnalysisInfo; struct AsmAnalysisInfo;
struct Dialect; struct Dialect;
struct CodeWeights;
} }
@ -78,7 +79,7 @@ public:
static std::variant<Program, langutil::ErrorList> load(langutil::CharStream& _sourceCode); static std::variant<Program, langutil::ErrorList> load(langutil::CharStream& _sourceCode);
void optimise(std::vector<std::string> const& _optimisationSteps); void optimise(std::vector<std::string> const& _optimisationSteps);
size_t codeSize() const { return computeCodeSize(*m_ast); } size_t codeSize(yul::CodeWeights const& _weights) const { return computeCodeSize(*m_ast, _weights); }
yul::Block const& ast() const { return *m_ast; } yul::Block const& ast() const { return *m_ast; }
friend std::ostream& operator<<(std::ostream& _stream, Program const& _program); friend std::ostream& operator<<(std::ostream& _stream, Program const& _program);
@ -113,7 +114,7 @@ private:
std::unique_ptr<yul::Block> _ast, std::unique_ptr<yul::Block> _ast,
std::vector<std::string> const& _optimisationSteps std::vector<std::string> const& _optimisationSteps
); );
static size_t computeCodeSize(yul::Block const& _ast); static size_t computeCodeSize(yul::Block const& _ast, yul::CodeWeights const& _weights);
std::unique_ptr<yul::Block> m_ast; std::unique_ptr<yul::Block> m_ast;
yul::Dialect const& m_dialect; yul::Dialect const& m_dialect;

View File

@ -17,6 +17,8 @@
#include <tools/yulPhaser/ProgramCache.h> #include <tools/yulPhaser/ProgramCache.h>
#include <libyul/optimiser/Metrics.h>
#include <libyul/optimiser/Suite.h> #include <libyul/optimiser/Suite.h>
using namespace std; using namespace std;
@ -133,7 +135,7 @@ size_t ProgramCache::calculateTotalCachedCodeSize() const
{ {
size_t size = 0; size_t size = 0;
for (auto const& pair: m_entries) for (auto const& pair: m_entries)
size += pair.second.program.codeSize(); size += pair.second.program.codeSize(CacheStats::StorageWeights);
return size; return size;
} }

View File

@ -19,6 +19,8 @@
#include <tools/yulPhaser/Program.h> #include <tools/yulPhaser/Program.h>
#include <libyul/optimiser/Metrics.h>
#include <map> #include <map>
#include <string> #include <string>
@ -44,6 +46,29 @@ struct CacheEntry
*/ */
struct CacheStats struct CacheStats
{ {
/// Weights used to compute totalCodeSize.
/// The goal here is to get a result proportional to the amount of memory taken by the AST.
/// Each statement/expression gets 1 just for existing. We add more if it contains any extra
/// data that won't be visited separately by ASTWalker.
static yul::CodeWeights constexpr StorageWeights = {
/* expressionStatementCost = */ 1,
/* assignmentCost = */ 1,
/* variableDeclarationCost = */ 1,
/* functionDefinitionCost = */ 1,
/* ifCost = */ 1,
/* switchCost = */ 1,
/* caseCost = */ 1,
/* forLoopCost = */ 1,
/* breakCost = */ 1,
/* continueCost = */ 1,
/* leaveCost = */ 1,
/* blockCost = */ 1,
/* functionCallCost = */ 1,
/* identifierCost = */ 1,
/* literalCost = */ 1,
};
size_t hits; size_t hits;
size_t misses; size_t misses;
size_t totalCodeSize; size_t totalCodeSize;