Merge pull request #11316 from ethereum/verbatimdata-tests

Added a few optimizer tests for Verbatim
This commit is contained in:
chriseth 2021-04-28 10:19:19 +02:00 committed by GitHub
commit 2cd0bb11dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 222 additions and 0 deletions

View File

@ -93,6 +93,45 @@ namespace
BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end());
}
/// In contrast to the function `CSE`, this function doesn't finish the CSE optimization on an
/// instruction that breaks CSE Analysis block. Copied from Assembly.cpp
AssemblyItems fullCSE(AssemblyItems const& _input)
{
AssemblyItems optimisedItems;
bool usesMSize = ranges::any_of(_input, [](AssemblyItem const& _i) {
return _i == AssemblyItem{Instruction::MSIZE} || _i.type() == VerbatimBytecode;
});
auto iter = _input.begin();
while (iter != _input.end())
{
KnownState emptyState;
CommonSubexpressionEliminator eliminator{emptyState};
auto orig = iter;
iter = eliminator.feedItems(iter, _input.end(), usesMSize);
bool shouldReplace = false;
AssemblyItems optimisedChunk;
optimisedChunk = eliminator.getOptimizedItems();
shouldReplace = (optimisedChunk.size() < static_cast<size_t>(iter - orig));
if (shouldReplace)
optimisedItems += optimisedChunk;
else
copy(orig, iter, back_inserter(optimisedItems));
}
return optimisedItems;
}
void checkFullCSE(
AssemblyItems const& _input,
AssemblyItems const& _expectation
)
{
AssemblyItems output = fullCSE(_input);
BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end());
}
AssemblyItems CFG(AssemblyItems const& _input)
{
AssemblyItems output = _input;
@ -1292,6 +1331,135 @@ BOOST_AUTO_TEST_CASE(cse_sub_zero)
});
}
BOOST_AUTO_TEST_CASE(cse_simple_verbatim)
{
auto verbatim = AssemblyItem{bytes{1, 2, 3, 4, 5}, 0, 0};
AssemblyItems input{verbatim};
checkCSE(input, input);
checkFullCSE(input, input);
}
BOOST_AUTO_TEST_CASE(cse_mload_pop)
{
AssemblyItems input{
u256(1000),
Instruction::MLOAD,
Instruction::POP,
};
AssemblyItems output{
};
checkCSE(input, output);
checkFullCSE(input, output);
}
BOOST_AUTO_TEST_CASE(cse_verbatim_mload)
{
auto verbatim = AssemblyItem{bytes{1, 2, 3, 4, 5}, 0, 0};
AssemblyItems input{
u256(1000),
Instruction::MLOAD, // Should not be removed
Instruction::POP,
verbatim,
u256(1000),
Instruction::MLOAD, // Should not be removed
Instruction::POP,
};
checkFullCSE(input, input);
}
BOOST_AUTO_TEST_CASE(cse_sload_verbatim_dup)
{
auto verbatim = AssemblyItem{bytes{1, 2, 3, 4, 5}, 0, 0};
AssemblyItems input{
u256(0),
Instruction::SLOAD,
u256(0),
Instruction::SLOAD,
verbatim
};
AssemblyItems output{
u256(0),
Instruction::SLOAD,
Instruction::DUP1,
verbatim
};
checkCSE(input, output);
checkFullCSE(input, output);
}
BOOST_AUTO_TEST_CASE(cse_verbatim_sload_sideeffect)
{
auto verbatim = AssemblyItem{bytes{1, 2, 3, 4, 5}, 0, 0};
AssemblyItems input{
u256(0),
Instruction::SLOAD,
verbatim,
u256(0),
Instruction::SLOAD,
};
checkFullCSE(input, input);
}
BOOST_AUTO_TEST_CASE(cse_verbatim_eq)
{
auto verbatim = AssemblyItem{bytes{1, 2, 3, 4, 5}, 0, 0};
AssemblyItems input{
u256(0),
Instruction::SLOAD,
verbatim,
Instruction::DUP1,
Instruction::EQ
};
checkFullCSE(input, input);
}
BOOST_AUTO_TEST_CASE(verbatim_knownstate)
{
KnownState state = createInitialState(AssemblyItems{
Instruction::DUP1,
Instruction::DUP2,
Instruction::DUP3,
Instruction::DUP4
});
map<int, unsigned> const& stackElements = state.stackElements();
BOOST_CHECK(state.stackHeight() == 4);
// One more than stack height because of the initial unknown element.
BOOST_CHECK(stackElements.size() == 5);
BOOST_CHECK(stackElements.count(0));
unsigned initialElement = stackElements.at(0);
// Check if all the DUPs were correctly matched to the same class.
for (auto const& height: {1, 2, 3, 4})
BOOST_CHECK(stackElements.at(height) == initialElement);
auto verbatim2i5o = AssemblyItem{bytes{1, 2, 3, 4, 5}, 2, 5};
state.feedItem(verbatim2i5o);
BOOST_CHECK(state.stackHeight() == 7);
// Stack elements
// Before verbatim: {{0, x}, {1, x}, {2, x}, {3, x}, {4, x}}
// After verbatim: {{0, x}, {1, x}, {2, x}, {3, a}, {4, b}, {5, c}, {6, d}, {7, e}}
BOOST_CHECK(stackElements.size() == 8);
for (auto const& height: {1, 2})
BOOST_CHECK(stackElements.at(height) == initialElement);
for (auto const& height: {3, 4, 5, 6, 7})
BOOST_CHECK(stackElements.at(height) != initialElement);
for (auto const& height1: {3, 4, 5, 6, 7})
for (auto const& height2: {3, 4, 5, 6, 7})
if (height1 < height2)
BOOST_CHECK(stackElements.at(height1) != stackElements.at(height2));
}
BOOST_AUTO_TEST_CASE(cse_remove_redundant_shift_masking)
{
if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting())

View File

@ -0,0 +1,16 @@
{
mstore(10, 20)
// cannot be resolved because of verbatim
sstore(0, mload(10))
verbatim_0i_0o("test")
}
// ----
// step: loadResolver
//
// {
// let _1 := 20
// let _2 := 10
// mstore(_2, _1)
// sstore(0, mload(_2))
// verbatim_0i_0o("test")
// }

View File

@ -0,0 +1,21 @@
{
sstore(10, 20)
// will be resolved
sstore(30, sload(10))
verbatim_0i_0o("test")
// will not be resolved
sstore(30, sload(10))
}
// ----
// step: loadResolver
//
// {
// let _1 := 20
// let _2 := 10
// sstore(_2, _1)
// let _4 := _1
// let _5 := 30
// sstore(_5, _4)
// verbatim_0i_0o("test")
// sstore(_5, sload(_2))
// }

View File

@ -0,0 +1,17 @@
{
// cannot be removed because of verbatim
let a := mload(10)
// cannot be removed because of verbatim
let b := keccak256(10, 32)
// can be removed
let c := add(a, b)
verbatim_0i_0o("test")
}
// ----
// step: unusedPruner
//
// {
// pop(mload(10))
// pop(keccak256(10, 32))
// verbatim_0i_0o("test")
// }