Optimizer rules that combine shifts and masks.

This commit is contained in:
chriseth 2019-04-16 13:14:13 +02:00
parent 66cea5abb6
commit f6437a2016
10 changed files with 166 additions and 40 deletions

View File

@ -5,6 +5,7 @@ Language Features:
Compiler Features:
* Optimizer: Add rule to simplify SHL/SHR combinations.
* SMTChecker: Support inherited state variables.
* SMTChecker: Support tuples and function calls with multiple return values.
* SMTChecker: Support ``delete``.

View File

@ -188,12 +188,6 @@ inline bytes toCompactBigEndian(uint8_t _val, unsigned _min = 0)
return (_min || _val) ? bytes{ _val } : bytes{};
}
/// Workarounds shift left bug in boost <1.65.1.
template <class S> S bigintShiftLeftWorkaround(S const& _a, unsigned _b)
{
return (S)(bigint(_a) << _b);
}
/// Convenience function for conversion of a u256 to hex
inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd)
{

View File

@ -47,6 +47,13 @@ template <class S> S modWorkaround(S const& _a, S const& _b)
return (S)(bigint(_a) % bigint(_b));
}
// This works around a bug fixed with Boost 1.64.
// https://www.boost.org/doc/libs/1_68_0/libs/multiprecision/doc/html/boost_multiprecision/map/hist.html#boost_multiprecision.map.hist.multiprecision_2_3_1_boost_1_64
inline u256 shlWorkaround(u256 const& _x, unsigned _amount)
{
return u256((bigint(_x) << _amount) & u256(-1));
}
// simplificationRuleList below was split up into parts to prevent
// stack overflows in the JavaScript optimizer for emscripten builds
// that affected certain browser versions.
@ -93,7 +100,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
{{Instruction::SHL, {A, B}}, [=]{
if (A.d() > 255)
return u256(0);
return bigintShiftLeftWorkaround(B.d(), unsigned(A.d()));
return shlWorkaround(B.d(), unsigned(A.d()));
}, false},
{{Instruction::SHR, {A, B}}, [=]{
if (A.d() > 255)
@ -365,6 +372,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
}
}
// Combine two SHL by constant
rules.push_back({
// SHL(B, SHL(A, X)) -> SHL(min(A+B, 256), X)
{Instruction::SHL, {{B}, {Instruction::SHL, {{A}, {X}}}}},
@ -378,6 +386,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
false
});
// Combine two SHR by constant
rules.push_back({
// SHR(B, SHR(A, X)) -> SHR(min(A+B, 256), X)
{Instruction::SHR, {{B}, {Instruction::SHR, {{A}, {X}}}}},
@ -391,6 +400,68 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
false
});
// Combine SHL-SHR by constant
rules.push_back({
// SHR(B, SHL(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
{Instruction::SHR, {{B}, {Instruction::SHL, {{A}, {X}}}}},
[=]() -> Pattern {
u256 mask = shlWorkaround(u256(-1), unsigned(A.d())) >> unsigned(B.d());
if (A.d() > B.d())
return {Instruction::AND, {{Instruction::SHL, {A.d() - B.d(), X}}, mask}};
else if (B.d() > A.d())
return {Instruction::AND, {{Instruction::SHR, {B.d() - A.d(), X}}, mask}};
else
return {Instruction::AND, {X, mask}};
},
false,
[=] { return A.d() < 256 && B.d() < 256; }
});
// Combine SHR-SHL by constant
rules.push_back({
// SHL(B, SHR(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
{Instruction::SHL, {{B}, {Instruction::SHR, {{A}, {X}}}}},
[=]() -> Pattern {
u256 mask = shlWorkaround(u256(-1) >> unsigned(A.d()), unsigned(B.d()));
if (A.d() > B.d())
return {Instruction::AND, {{Instruction::SHR, {A.d() - B.d(), X}}, mask}};
else if (B.d() > A.d())
return {Instruction::AND, {{Instruction::SHL, {B.d() - A.d(), X}}, mask}};
else
return {Instruction::AND, {X, mask}};
},
false,
[=] { return A.d() < 256 && B.d() < 256; }
});
// Move AND with constant across SHL and SHR by constant
for (auto shiftOp: {Instruction::SHL, Instruction::SHR})
{
auto replacement = [=]() -> Pattern {
u256 mask =
shiftOp == Instruction::SHL ?
shlWorkaround(A.d(), unsigned(B.d())) :
A.d() >> unsigned(B.d());
return {Instruction::AND, {{shiftOp, {B.d(), X}}, std::move(mask)}};
};
rules.push_back({
// SH[L/R](B, AND(X, A)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
{shiftOp, {{B}, {Instruction::AND, {{X}, {A}}}}},
replacement,
false,
[=] { return B.d() < 256; }
});
rules.push_back({
// SH[L/R](B, AND(A, X)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
{shiftOp, {{B}, {Instruction::AND, {{A}, {X}}}}},
replacement,
false,
[=] { return B.d() < 256; }
});
}
rules.push_back({
// MUL(X, SHL(Y, 1)) -> SHL(Y, X)
{Instruction::MUL, {X, {Instruction::SHL, {Y, u256(1)}}}},

View File

@ -27,29 +27,29 @@ contract Large {
// optimize-runs: 2
// ----
// creation:
// codeDepositCost: 261800
// codeDepositCost: 260400
// executionCost: 300
// totalCost: 262100
// totalCost: 260700
// external:
// a(): 398
// b(uint256): 1105
// f0(uint256): 334
// f1(uint256): 40886
// f2(uint256): 20952
// f3(uint256): 21040
// f4(uint256): 21018
// f5(uint256): 20996
// f6(uint256): 20908
// f7(uint256): 20688
// f8(uint256): 20820
// f9(uint256): 20842
// f1(uint256): 40874
// f2(uint256): 20940
// f3(uint256): 21028
// f4(uint256): 21006
// f5(uint256): 20984
// f6(uint256): 20896
// f7(uint256): 20676
// f8(uint256): 20808
// f9(uint256): 20830
// g0(uint256): 574
// g1(uint256): 40598
// g2(uint256): 20686
// g3(uint256): 20774
// g4(uint256): 20752
// g5(uint256): 20840
// g6(uint256): 20620
// g7(uint256): 20730
// g8(uint256): 20708
// g9(uint256): 20554
// g1(uint256): 40586
// g2(uint256): 20674
// g3(uint256): 20762
// g4(uint256): 20740
// g5(uint256): 20828
// g6(uint256): 20608
// g7(uint256): 20718
// g8(uint256): 20696
// g9(uint256): 20542

View File

@ -14,16 +14,16 @@ contract Medium {
// optimize-runs: 2
// ----
// creation:
// codeDepositCost: 142200
// executionCost: 190
// totalCost: 142390
// codeDepositCost: 140800
// executionCost: 183
// totalCost: 140983
// external:
// a(): 398
// b(uint256): 863
// f1(uint256): 40666
// f2(uint256): 20710
// f3(uint256): 20754
// f1(uint256): 40654
// f2(uint256): 20698
// f3(uint256): 20742
// g0(uint256): 332
// g7(uint256): 20620
// g8(uint256): 20598
// g9(uint256): 20554
// g7(uint256): 20608
// g8(uint256): 20586
// g9(uint256): 20542

View File

@ -9,11 +9,11 @@ contract Small {
// optimize-runs: 2
// ----
// creation:
// codeDepositCost: 65400
// executionCost: 117
// totalCost: 65517
// codeDepositCost: 60400
// executionCost: 111
// totalCost: 60511
// external:
// fallback: 118
// a(): 376
// b(uint256): 753
// f1(uint256): 40600
// f1(uint256): 40588

View File

@ -188,6 +188,8 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
{
disambiguate();
ExpressionSimplifier::run(*m_dialect, *m_ast);
ExpressionSimplifier::run(*m_dialect, *m_ast);
ExpressionSimplifier::run(*m_dialect, *m_ast);
}
else if (m_optimizerStep == "fullSimplify")
{

View File

@ -0,0 +1,14 @@
{
let x := calldataload(0)
let a := and(0xff, shr(248, shl(248, shr(248, x))))
let b := shr(12, shl(8, and(x, 0xf0f0)))
}
// ====
// EVMVersion: >byzantium
// step: expressionSimplifier
// ----
// {
// let x := calldataload(0)
// let a := shr(248, x)
// let b := and(shr(4, x), 3855)
// }

View File

@ -0,0 +1,24 @@
{
let x := calldataload(0)
let a := and(0xff, shr(248, shl(248, shr(248, and(x, 0xf)))))
let b := shl(12, shr(4, and(x, 0xf0f0)))
let c := shl(12, shr(4, and(0xf0f0, x)))
let d := shl(12, shr(255, and(0xf0f0, x)))
let e := shl(255, shr(4, and(0xf0f0, x)))
let f := shl(12, shr(256, and(0xf0f0, x)))
let g := shl(256, shr(4, and(0xf0f0, x)))
}
// ====
// EVMVersion: >byzantium
// step: expressionSimplifier
// ----
// {
// let x := calldataload(0)
// let a := 0
// let b := and(shl(8, x), 15790080)
// let c := and(shl(8, x), 15790080)
// let d := 0
// let e := and(shl(251, x), 0x8000000000000000000000000000000000000000000000000000000000000000)
// let f := 0
// let g := 0
// }

View File

@ -0,0 +1,20 @@
{
let x := calldataload(0)
let a := shl(12, shr(4, x))
let b := shl(4, shr(12, x))
let c := shr(12, shl(4, x))
let d := shr(4, shl(12, x))
let e := shl(150, shr(2, shl(150, x)))
}
// ====
// EVMVersion: >byzantium
// step: expressionSimplifier
// ----
// {
// let x := calldataload(0)
// let a := and(shl(8, x), 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000)
// let b := and(shr(8, x), 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0)
// let c := and(shr(8, x), 0x0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
// let d := and(shl(8, x), 0x0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00)
// let e := 0
// }