Skip to content

Commit 4c4860b

Browse files
authored
Merge pull request #15991 from ipsilon/enable-peephole-optimiser
eof: Enable peephole optimizer for EOF.
2 parents 6d62ea0 + 03fde2a commit 4c4860b

File tree

7 files changed

+201
-11
lines changed

7 files changed

+201
-11
lines changed

libevmasm/Assembly.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -843,8 +843,7 @@ std::map<u256, u256> const& Assembly::optimiseInternal(
843843
}
844844
}
845845

846-
// TODO: verify this for EOF.
847-
if (_settings.runPeephole && !m_eofVersion.has_value())
846+
if (_settings.runPeephole)
848847
{
849848
for (auto& codeSection: m_codeSections)
850849
{

libevmasm/AssemblyItem.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ class AssemblyItem
244244
/// Shortcut that avoids constructing an AssemblyItem just to perform the comparison.
245245
bool operator==(Instruction _instr) const
246246
{
247-
return type() == Operation && instruction() == _instr;
247+
return hasInstruction() && instruction() == _instr;
248248
}
249249
bool operator!=(Instruction _instr) const { return !operator==(_instr); }
250250

libevmasm/PeepholeOptimiser.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,9 @@ struct TruthyAnd: SimplePeepholeOptimizerMethod<TruthyAnd>
563563
}
564564
};
565565

566-
/// Removes everything after a JUMP (or similar) until the next JUMPDEST.
566+
/// Removes everything after an non-continuing instruction until the next Tag.
567+
/// Note: JUMPF can return but to the caller's parent call frame.
568+
/// So it won't continue from the next to the JUMPF instruction
567569
struct UnreachableCode
568570
{
569571
static bool apply(OptimiserState& _state)
@@ -572,14 +574,18 @@ struct UnreachableCode
572574
auto end = _state.items.end();
573575
if (it == end)
574576
return false;
577+
575578
if (
576579
it[0] != Instruction::JUMP &&
577-
it[0] != Instruction::RJUMP &&
578580
it[0] != Instruction::RETURN &&
579581
it[0] != Instruction::STOP &&
580582
it[0] != Instruction::INVALID &&
581583
it[0] != Instruction::SELFDESTRUCT &&
582-
it[0] != Instruction::REVERT
584+
it[0] != Instruction::REVERT &&
585+
it[0] != Instruction::RJUMP &&
586+
it[0] != Instruction::JUMPF &&
587+
it[0] != Instruction::RETF &&
588+
it[0] != Instruction::RETURNCONTRACT
583589
)
584590
return false;
585591

test/Common.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,13 @@ boost::unit_test::precondition::predicate_t nonEOF()
313313
};
314314
}
315315

316+
boost::unit_test::precondition::predicate_t onEOF()
317+
{
318+
return [](boost::unit_test::test_unit_id) {
319+
return solidity::test::CommonOptions::get().eofVersion().has_value();
320+
};
321+
}
322+
316323
boost::unit_test::precondition::predicate_t minEVMVersionCheck(langutil::EVMVersion _minEVMVersion)
317324
{
318325
return [_minEVMVersion](boost::unit_test::test_unit_id) {

test/Common.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ boost::unit_test::precondition::predicate_t minEVMVersionCheck(langutil::EVMVers
116116
/// @return A predicate (function) that can be passed into @a boost::unit_test::precondition().
117117
boost::unit_test::precondition::predicate_t nonEOF();
118118

119+
/// Helper that can be used to skip tests when the legacy bytecode is not supported by the test case.
120+
/// @return A predicate (function) that can be passed into @a boost::unit_test::precondition().
121+
boost::unit_test::precondition::predicate_t onEOF();
122+
119123
bool loadVMs(CommonOptions const& _options);
120124

121125
/**

test/libevmasm/Optimiser.cpp

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,182 @@ BOOST_AUTO_TEST_CASE(clear_unreachable_code)
10091009
);
10101010
}
10111011

1012+
BOOST_AUTO_TEST_CASE(clear_unreachable_code_eof, *boost::unit_test::precondition(onEOF()))
1013+
{
1014+
for (auto const& blockTerminatingItem:
1015+
{
1016+
AssemblyItem::relativeJumpTo(AssemblyItem(Tag, 1)),
1017+
AssemblyItem::jumpToFunction(1, 0, 0),
1018+
AssemblyItem::functionReturn(),
1019+
AssemblyItem::returnContract(0),
1020+
}
1021+
)
1022+
{
1023+
AssemblyItems items{
1024+
blockTerminatingItem,
1025+
u256(0),
1026+
Instruction::SLOAD,
1027+
AssemblyItem(Tag, 2),
1028+
u256(5),
1029+
u256(6),
1030+
Instruction::SSTORE,
1031+
blockTerminatingItem,
1032+
u256(5),
1033+
u256(6)
1034+
};
1035+
AssemblyItems expectation{
1036+
blockTerminatingItem,
1037+
AssemblyItem(Tag, 2),
1038+
u256(5),
1039+
u256(6),
1040+
Instruction::SSTORE,
1041+
blockTerminatingItem,
1042+
};
1043+
PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion());
1044+
BOOST_REQUIRE(peepOpt.optimise());
1045+
BOOST_CHECK_EQUAL_COLLECTIONS(
1046+
items.begin(), items.end(),
1047+
expectation.begin(), expectation.end()
1048+
);
1049+
}
1050+
}
1051+
1052+
BOOST_AUTO_TEST_CASE(is_zero_is_zero_rjumpi, *boost::unit_test::precondition(onEOF()))
1053+
{
1054+
AssemblyItems items{
1055+
u256(1),
1056+
Instruction::ISZERO,
1057+
Instruction::ISZERO,
1058+
AssemblyItem::conditionalRelativeJumpTo(AssemblyItem(Tag, 1)),
1059+
u256(0),
1060+
Instruction::SLOAD,
1061+
AssemblyItem(Tag, 1),
1062+
};
1063+
1064+
AssemblyItems expectation{
1065+
u256(1),
1066+
AssemblyItem::conditionalRelativeJumpTo(AssemblyItem(Tag, 1)),
1067+
u256(0),
1068+
Instruction::SLOAD,
1069+
AssemblyItem(Tag, 1),
1070+
};
1071+
1072+
PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion());
1073+
BOOST_REQUIRE(peepOpt.optimise());
1074+
BOOST_CHECK_EQUAL_COLLECTIONS(
1075+
items.begin(), items.end(),
1076+
expectation.begin(), expectation.end()
1077+
);
1078+
}
1079+
1080+
BOOST_AUTO_TEST_CASE(equal_is_zero_rjumpi, *boost::unit_test::precondition(onEOF()))
1081+
{
1082+
AssemblyItems items{
1083+
u256(1),
1084+
u256(2),
1085+
Instruction::EQ,
1086+
Instruction::ISZERO,
1087+
AssemblyItem::conditionalRelativeJumpTo(AssemblyItem(Tag, 1)),
1088+
u256(0),
1089+
Instruction::SLOAD,
1090+
AssemblyItem(Tag, 1),
1091+
};
1092+
1093+
AssemblyItems expectation{
1094+
u256(1),
1095+
u256(2),
1096+
Instruction::SUB,
1097+
AssemblyItem::conditionalRelativeJumpTo(AssemblyItem(Tag, 1)),
1098+
u256(0),
1099+
Instruction::SLOAD,
1100+
AssemblyItem(Tag, 1),
1101+
};
1102+
1103+
PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion());
1104+
BOOST_REQUIRE(peepOpt.optimise());
1105+
BOOST_CHECK_EQUAL_COLLECTIONS(
1106+
items.begin(), items.end(),
1107+
expectation.begin(), expectation.end()
1108+
);
1109+
}
1110+
1111+
BOOST_AUTO_TEST_CASE(double_rjump, *boost::unit_test::precondition(onEOF()))
1112+
{
1113+
AssemblyItems items{
1114+
u256(1),
1115+
AssemblyItem::conditionalRelativeJumpTo(AssemblyItem(Tag, 1)),
1116+
AssemblyItem::relativeJumpTo(AssemblyItem(Tag, 2)),
1117+
AssemblyItem(Tag, 1),
1118+
u256(0),
1119+
Instruction::SLOAD,
1120+
AssemblyItem(Tag, 2),
1121+
};
1122+
1123+
AssemblyItems expectation{
1124+
u256(1),
1125+
Instruction::ISZERO,
1126+
AssemblyItem::conditionalRelativeJumpTo(AssemblyItem(Tag, 2)),
1127+
AssemblyItem(Tag, 1),
1128+
u256(0),
1129+
Instruction::SLOAD,
1130+
AssemblyItem(Tag, 2),
1131+
};
1132+
1133+
PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion());
1134+
BOOST_REQUIRE(peepOpt.optimise());
1135+
BOOST_CHECK_EQUAL_COLLECTIONS(
1136+
items.begin(), items.end(),
1137+
expectation.begin(), expectation.end()
1138+
);
1139+
}
1140+
1141+
BOOST_AUTO_TEST_CASE(rjump_to_next, *boost::unit_test::precondition(onEOF()))
1142+
{
1143+
AssemblyItems items{
1144+
AssemblyItem::relativeJumpTo(AssemblyItem(Tag, 1)),
1145+
AssemblyItem(Tag, 1),
1146+
u256(0),
1147+
Instruction::SLOAD,
1148+
};
1149+
1150+
AssemblyItems expectation{
1151+
AssemblyItem(Tag, 1),
1152+
u256(0),
1153+
Instruction::SLOAD,
1154+
};
1155+
1156+
PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion());
1157+
BOOST_REQUIRE(peepOpt.optimise());
1158+
BOOST_CHECK_EQUAL_COLLECTIONS(
1159+
items.begin(), items.end(),
1160+
expectation.begin(), expectation.end()
1161+
);
1162+
}
1163+
1164+
BOOST_AUTO_TEST_CASE(rjumpi_to_next, *boost::unit_test::precondition(onEOF()))
1165+
{
1166+
AssemblyItems items{
1167+
AssemblyItem::conditionalRelativeJumpTo(AssemblyItem(Tag, 1)),
1168+
AssemblyItem(Tag, 1),
1169+
u256(0),
1170+
Instruction::SLOAD,
1171+
};
1172+
1173+
AssemblyItems expectation{
1174+
Instruction::POP,
1175+
AssemblyItem(Tag, 1),
1176+
u256(0),
1177+
Instruction::SLOAD,
1178+
};
1179+
1180+
PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion());
1181+
BOOST_REQUIRE(peepOpt.optimise());
1182+
BOOST_CHECK_EQUAL_COLLECTIONS(
1183+
items.begin(), items.end(),
1184+
expectation.begin(), expectation.end()
1185+
);
1186+
}
1187+
10121188
BOOST_AUTO_TEST_CASE(deduplicateNextTagBlockSize3)
10131189
{
10141190
AssemblyItems items{

0 commit comments

Comments
 (0)