mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Inline ordinary jumps to small blocks and jumps to terminating control flow.
This commit is contained in:
parent
a99eb17608
commit
8b3095920a
@ -4,6 +4,7 @@ Language Features:
|
||||
* Possibility to use ``bytes.concat`` with variable number of ``bytes`` and ``bytesNN`` arguments which behaves as a restricted version of `abi.encodePacked` with a more descriptive name.
|
||||
|
||||
Compiler Features:
|
||||
* Low-Level Inliner: Inline ordinary jumps to small blocks and jumps to small blocks that terminate.
|
||||
|
||||
|
||||
Bugfixes:
|
||||
|
@ -82,9 +82,7 @@ bool Inliner::isInlineCandidate(size_t _tag, ranges::span<AssemblyItem const> _i
|
||||
{
|
||||
assertThrow(_items.size() > 0, OptimizerException, "");
|
||||
|
||||
// Only consider blocks that end in a JUMP for now. This can e.g. be extended to include transaction terminating
|
||||
// instructions as well in the future.
|
||||
if (_items.back() != Instruction::JUMP)
|
||||
if (_items.back() != Instruction::JUMP && !SemanticInformation::terminatesControlFlow(_items.back()))
|
||||
return false;
|
||||
|
||||
// Never inline tags that reference themselves.
|
||||
@ -196,19 +194,38 @@ bool Inliner::shouldInlineFullFunctionBody(size_t _tag, ranges::span<AssemblyIte
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
optional<AssemblyItem::JumpType> Inliner::shouldInline(size_t _tag, AssemblyItem const& _jump, InlinableBlock const& _block) const
|
||||
optional<AssemblyItem> Inliner::shouldInline(size_t _tag, AssemblyItem const& _jump, InlinableBlock const& _block) const
|
||||
{
|
||||
AssemblyItem exitJump = _block.items.back();
|
||||
assertThrow(_jump == Instruction::JUMP && exitJump == Instruction::JUMP, OptimizerException, "");
|
||||
assertThrow(_jump == Instruction::JUMP, OptimizerException, "");
|
||||
AssemblyItem blockExit = _block.items.back();
|
||||
|
||||
if (
|
||||
_jump.getJumpType() == AssemblyItem::JumpType::IntoFunction &&
|
||||
exitJump.getJumpType() == AssemblyItem::JumpType::OutOfFunction
|
||||
blockExit == Instruction::JUMP &&
|
||||
blockExit.getJumpType() == AssemblyItem::JumpType::OutOfFunction &&
|
||||
shouldInlineFullFunctionBody(_tag, _block.items, _block.pushTagCount)
|
||||
)
|
||||
return
|
||||
shouldInlineFullFunctionBody(_tag, _block.items, _block.pushTagCount) ?
|
||||
make_optional(AssemblyItem::JumpType::Ordinary) : nullopt;
|
||||
{
|
||||
blockExit.setJumpType(AssemblyItem::JumpType::Ordinary);
|
||||
return blockExit;
|
||||
}
|
||||
|
||||
// Inline small blocks, if the jump to it is ordinary or the blockExit is a terminating instruction.
|
||||
if (
|
||||
_jump.getJumpType() == AssemblyItem::JumpType::Ordinary ||
|
||||
SemanticInformation::terminatesControlFlow(blockExit)
|
||||
)
|
||||
{
|
||||
static AssemblyItems const jumpPattern = {
|
||||
AssemblyItem{PushTag},
|
||||
AssemblyItem{Instruction::JUMP},
|
||||
};
|
||||
if (
|
||||
GasMeter::dataGas(codeSize(_block.items), m_isCreation, m_evmVersion) <=
|
||||
GasMeter::dataGas(codeSize(jumpPattern), m_isCreation, m_evmVersion)
|
||||
)
|
||||
return blockExit;
|
||||
}
|
||||
|
||||
return nullopt;
|
||||
}
|
||||
@ -232,10 +249,10 @@ void Inliner::optimise()
|
||||
{
|
||||
if (optional<size_t> tag = getLocalTag(item))
|
||||
if (auto* inlinableBlock = util::valueOrNullptr(inlinableBlocks, *tag))
|
||||
if (auto exitJumpType = shouldInline(*tag, nextItem, *inlinableBlock))
|
||||
if (auto exitItem = shouldInline(*tag, nextItem, *inlinableBlock))
|
||||
{
|
||||
newItems += inlinableBlock->items;
|
||||
newItems.back().setJumpType(*exitJumpType);
|
||||
newItems += inlinableBlock->items | ranges::views::drop_last(1);
|
||||
newItems.emplace_back(move(*exitItem));
|
||||
|
||||
// We are removing one push tag to the block we inline.
|
||||
--inlinableBlock->pushTagCount;
|
||||
|
@ -61,8 +61,8 @@ private:
|
||||
uint64_t pushTagCount = 0;
|
||||
};
|
||||
|
||||
/// @returns the exit jump type for the block to be inlined, if a particular jump to it should be inlined, otherwise nullopt.
|
||||
std::optional<AssemblyItem::JumpType> shouldInline(size_t _tag, AssemblyItem const& _jump, InlinableBlock const& _block) const;
|
||||
/// @returns the exit item for the block to be inlined, if a particular jump to it should be inlined, otherwise nullopt.
|
||||
std::optional<AssemblyItem> shouldInline(size_t _tag, AssemblyItem const& _jump, InlinableBlock const& _block) const;
|
||||
/// @returns true, if the full function at tag @a _tag with body @a _block that is referenced @a _pushTagCount times
|
||||
/// should be inlined, false otherwise. @a _block should start at the first instruction after the function entry tag
|
||||
/// up to and including the return jump.
|
||||
|
@ -10,7 +10,7 @@ EVM assembly:
|
||||
not(sub(shl(0x40, 0x01), 0x01))
|
||||
and
|
||||
/* "optimizer_BlockDeDuplicator/input.sol":201:206 fun_x */
|
||||
or(tag_0_7, shl(0x20, tag_4))
|
||||
or(tag_0_7, shl(0x20, tag_2))
|
||||
sub(shl(0x40, 0x01), 0x01)
|
||||
/* "optimizer_BlockDeDuplicator/input.sol":179:210 function() r = true ? fun_x : f */
|
||||
and
|
||||
@ -29,8 +29,8 @@ EVM assembly:
|
||||
tag_5:
|
||||
pop
|
||||
jump(tag_6)
|
||||
/* "optimizer_BlockDeDuplicator/input.sol":77:103 function fun_x() public {} */
|
||||
tag_4:
|
||||
/* "optimizer_BlockDeDuplicator/input.sol":138:174 function f() public { true ? 1 : 3;} */
|
||||
tag_2:
|
||||
jump // out
|
||||
/* "optimizer_BlockDeDuplicator/input.sol":60:213 contract C {... */
|
||||
tag_6:
|
||||
@ -66,12 +66,12 @@ sub_0: assembly {
|
||||
dup1
|
||||
0x2e1fb2bc
|
||||
eq
|
||||
tag_4
|
||||
tag_3
|
||||
jumpi
|
||||
dup1
|
||||
0x4753a67d
|
||||
eq
|
||||
tag_4
|
||||
tag_3
|
||||
jumpi
|
||||
tag_2:
|
||||
0x00
|
||||
@ -80,10 +80,6 @@ sub_0: assembly {
|
||||
/* "optimizer_BlockDeDuplicator/input.sol":138:174 function f() public { true ? 1 : 3;} */
|
||||
tag_3:
|
||||
stop
|
||||
/* "optimizer_BlockDeDuplicator/input.sol":108:133 function fun_() public {} */
|
||||
tag_4:
|
||||
jump(tag_3)
|
||||
/* "optimizer_BlockDeDuplicator/input.sol":138:174 function f() public { true ? 1 : 3;} */
|
||||
tag_7:
|
||||
jump // out
|
||||
|
||||
|
@ -58,9 +58,9 @@ sub_0: assembly {
|
||||
revert
|
||||
/* "optimizer_inliner_dynamic_reference/input.sol":160:215 function a() public pure returns (uint) { return f(); } */
|
||||
tag_3:
|
||||
tag_6
|
||||
tag_7
|
||||
jump // in
|
||||
/* "optimizer_inliner_dynamic_reference/input.sol":361:362 6 */
|
||||
0x06
|
||||
/* "optimizer_inliner_dynamic_reference/input.sol":160:215 function a() public pure returns (uint) { return f(); } */
|
||||
tag_6:
|
||||
mload(0x40)
|
||||
/* "#utility.yul":160:185 */
|
||||
@ -100,12 +100,6 @@ sub_0: assembly {
|
||||
sstore
|
||||
/* "optimizer_inliner_dynamic_reference/input.sol":125:155 function g() public { x = f; } */
|
||||
stop
|
||||
/* "optimizer_inliner_dynamic_reference/input.sol":160:215 function a() public pure returns (uint) { return f(); } */
|
||||
tag_7:
|
||||
/* "optimizer_inliner_dynamic_reference/input.sol":194:198 uint */
|
||||
0x00
|
||||
/* "optimizer_inliner_dynamic_reference/input.sol":361:362 6 */
|
||||
0x06
|
||||
/* "optimizer_inliner_dynamic_reference/input.sol":209:212 f() */
|
||||
tag_16:
|
||||
/* "optimizer_inliner_dynamic_reference/input.sol":202:212 return f() */
|
||||
@ -147,8 +141,8 @@ sub_0: assembly {
|
||||
tag_17:
|
||||
/* "optimizer_inliner_dynamic_reference/input.sol":361:362 6 */
|
||||
0x06
|
||||
/* "optimizer_inliner_dynamic_reference/input.sol":310:365 function f() internal pure returns (uint) { return 6; } */
|
||||
swap1
|
||||
/* "optimizer_inliner_dynamic_reference/input.sol":310:365 function f() internal pure returns (uint) { return 6; } */
|
||||
jump // out
|
||||
tag_20:
|
||||
mstore(0x00, shl(0xe0, 0x4e487b71))
|
||||
|
@ -35,8 +35,8 @@ tag_1:
|
||||
tag_4:
|
||||
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":355:356 6 */
|
||||
0x06
|
||||
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":304:359 function f() internal pure returns (uint) { return 6; } */
|
||||
swap1
|
||||
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":304:359 function f() internal pure returns (uint) { return 6; } */
|
||||
jump // out
|
||||
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":60:361 contract C {... */
|
||||
tag_5:
|
||||
@ -80,9 +80,9 @@ sub_0: assembly {
|
||||
revert
|
||||
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":154:209 function a() public pure returns (uint) { return f(); } */
|
||||
tag_3:
|
||||
tag_5
|
||||
tag_6
|
||||
jump // in
|
||||
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":355:356 6 */
|
||||
0x06
|
||||
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":154:209 function a() public pure returns (uint) { return f(); } */
|
||||
tag_5:
|
||||
mload(0x40)
|
||||
/* "#utility.yul":160:185 */
|
||||
@ -105,12 +105,6 @@ sub_0: assembly {
|
||||
tag_5
|
||||
tag_10
|
||||
jump // in
|
||||
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":154:209 function a() public pure returns (uint) { return f(); } */
|
||||
tag_6:
|
||||
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":188:192 uint */
|
||||
0x00
|
||||
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":355:356 6 */
|
||||
0x06
|
||||
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":203:206 f() */
|
||||
tag_14:
|
||||
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":196:206 return f() */
|
||||
@ -152,8 +146,8 @@ sub_0: assembly {
|
||||
tag_12:
|
||||
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":355:356 6 */
|
||||
0x06
|
||||
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":304:359 function f() internal pure returns (uint) { return 6; } */
|
||||
swap1
|
||||
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":304:359 function f() internal pure returns (uint) { return 6; } */
|
||||
jump // out
|
||||
tag_17:
|
||||
mstore(0x00, shl(0xe0, 0x4e487b71))
|
||||
|
@ -1637,6 +1637,27 @@ BOOST_AUTO_TEST_CASE(inliner_cse_break)
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(inliner_stop)
|
||||
{
|
||||
AssemblyItem jumpTo{Instruction::JUMP};
|
||||
AssemblyItems items{
|
||||
AssemblyItem(PushTag, 1),
|
||||
Instruction::JUMP,
|
||||
AssemblyItem(Tag, 1),
|
||||
Instruction::STOP
|
||||
};
|
||||
AssemblyItems expectation{
|
||||
Instruction::STOP,
|
||||
AssemblyItem(Tag, 1),
|
||||
Instruction::STOP
|
||||
};
|
||||
Inliner{items, {}, 200, false, {}}.optimise();
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(
|
||||
items.begin(), items.end(),
|
||||
expectation.begin(), expectation.end()
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
} // end namespaces
|
||||
|
@ -210,7 +210,7 @@ BOOST_AUTO_TEST_CASE(jump_type)
|
||||
jumpTypes += item.getJumpTypeAsString() + "\n";
|
||||
|
||||
if (solidity::test::CommonOptions::get().optimize)
|
||||
BOOST_CHECK_EQUAL(jumpTypes, "[in]\n[out]\n[in]\n[out]\n");
|
||||
BOOST_CHECK_EQUAL(jumpTypes, "[in]\n[out]\n[out]\n[in]\n[out]\n");
|
||||
else
|
||||
BOOST_CHECK_EQUAL(jumpTypes, "[in]\n[out]\n[in]\n[out]\n");
|
||||
}
|
||||
|
@ -21,4 +21,4 @@ contract c {
|
||||
// test() -> 0
|
||||
// gas irOptimized: 312322
|
||||
// gas legacy: 483915
|
||||
// gas legacyOptimized: 478673
|
||||
// gas legacyOptimized: 478672
|
||||
|
@ -14,7 +14,7 @@ contract Test {
|
||||
// set(uint24[]): 0x20, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 -> 18
|
||||
// gas irOptimized: 121109
|
||||
// gas legacy: 125815
|
||||
// gas legacyOptimized: 123615
|
||||
// gas legacyOptimized: 123614
|
||||
// data(uint256): 7 -> 8
|
||||
// data(uint256): 15 -> 16
|
||||
// data(uint256): 18 -> FAILURE
|
||||
|
@ -50,4 +50,4 @@ contract C {
|
||||
// f() -> 0xff
|
||||
// gas irOptimized: 137415
|
||||
// gas legacy: 137645
|
||||
// gas legacyOptimized: 134377
|
||||
// gas legacyOptimized: 134376
|
||||
|
@ -19,4 +19,4 @@ contract c {
|
||||
// test() -> 0
|
||||
// gas irOptimized: 397892
|
||||
// gas legacy: 565428
|
||||
// gas legacyOptimized: 552525
|
||||
// gas legacyOptimized: 552524
|
||||
|
@ -28,4 +28,4 @@ contract Creator {
|
||||
// f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8
|
||||
// gas irOptimized: 472714
|
||||
// gas legacy: 570900
|
||||
// gas legacyOptimized: 436360
|
||||
// gas legacyOptimized: 435524
|
||||
|
@ -298,4 +298,4 @@ contract Test {
|
||||
// verifyTx() -> true
|
||||
// gas irOptimized: 145824
|
||||
// gas legacy: 130571
|
||||
// gas legacyOptimized: 100187
|
||||
// gas legacyOptimized: 100147
|
||||
|
@ -24,4 +24,4 @@ contract A {
|
||||
// same_salt() -> true
|
||||
// gas irOptimized: 98439083
|
||||
// gas legacy: 98439116
|
||||
// gas legacyOptimized: 98438982
|
||||
// gas legacyOptimized: 98438970
|
||||
|
@ -38,4 +38,4 @@ contract C {
|
||||
// f(bytes): 0x20, 0x5, "abcde" -> 0
|
||||
// gas irOptimized: 248997
|
||||
// gas legacy: 239258
|
||||
// gas legacyOptimized: 238578
|
||||
// gas legacyOptimized: 238577
|
||||
|
@ -30,7 +30,7 @@ contract C {
|
||||
// index(uint256): 0xFF -> true
|
||||
// gas irOptimized: 167533
|
||||
// gas legacy: 248854
|
||||
// gas legacyOptimized: 152640
|
||||
// gas legacyOptimized: 152638
|
||||
// accessIndex(uint256,int256): 10, 1 -> 2
|
||||
// accessIndex(uint256,int256): 10, 0 -> 1
|
||||
// accessIndex(uint256,int256): 10, 11 -> FAILURE, hex"4e487b71", 0x32
|
||||
|
Loading…
Reference in New Issue
Block a user