diff --git a/Changelog.md b/Changelog.md index 223c692dd..0d648562f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Language Features: Compiler Features: + * Optimizer: Try to move ``and`` with constant inside ``or`` to improve storage writes of small types. Bugfixes: diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h index c324514ec..c2f2cdbb8 100644 --- a/libevmasm/RuleList.h +++ b/libevmasm/RuleList.h @@ -507,6 +507,23 @@ std::vector> simplificationRuleListPart7( }); } + // Combine alternating AND/OR/AND with constant, + // AND(OR(AND(X, A), Y), B) -> OR(AND(X, A & B), AND(Y, B)) + // Many versions due to commutativity. + for (auto const& inner: {Builtins::AND(X, A), Builtins::AND(A, X)}) + for (auto const& second: {Builtins::OR(inner, Y), Builtins::OR(Y, inner)}) + { + // We might swap X and Y but this is not an issue anymore. + rules.push_back({ + Builtins::AND(second, B), + [=]() -> Pattern { return Builtins::OR(Builtins::AND(X, A.d() & B.d()), Builtins::AND(Y, B)); } + }); + rules.push_back({ + Builtins::AND(B, second), + [=]() -> Pattern { return Builtins::OR(Builtins::AND(X, A.d() & B.d()), Builtins::AND(Y, B)); } + }); + } + rules.push_back({ // MUL(X, SHL(Y, 1)) -> SHL(Y, X) Builtins::MUL(X, Builtins::SHL(Y, Word(1))), diff --git a/test/cmdlineTests/optimize_full_storage_write/args b/test/cmdlineTests/optimize_full_storage_write/args new file mode 100644 index 000000000..8942fcc35 --- /dev/null +++ b/test/cmdlineTests/optimize_full_storage_write/args @@ -0,0 +1 @@ +--optimize --asm --metadata-hash none diff --git a/test/cmdlineTests/optimize_full_storage_write/input.sol b/test/cmdlineTests/optimize_full_storage_write/input.sol new file mode 100644 index 000000000..46db5ad3c --- /dev/null +++ b/test/cmdlineTests/optimize_full_storage_write/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract OptimizeFullSlotWrite { + uint64[4] nums; + function f() public { + nums[0] = 11111; + nums[1] = 22222; + nums[2] = 33333; + nums[3] = 44444; + } +} diff --git a/test/cmdlineTests/optimize_full_storage_write/output b/test/cmdlineTests/optimize_full_storage_write/output new file mode 100644 index 000000000..1a7322030 --- /dev/null +++ b/test/cmdlineTests/optimize_full_storage_write/output @@ -0,0 +1,64 @@ + +======= optimize_full_storage_write/input.sol:OptimizeFullSlotWrite ======= +EVM assembly: + /* "optimize_full_storage_write/input.sol":60:213 contract OptimizeFullSlotWrite {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + 0x00 + dup1 + revert +tag_1: + pop + dataSize(sub_0) + dup1 + dataOffset(sub_0) + 0x00 + codecopy + 0x00 + return +stop + +sub_0: assembly { + /* "optimize_full_storage_write/input.sol":60:213 contract OptimizeFullSlotWrite {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + 0x00 + dup1 + revert + tag_1: + pop + jumpi(tag_2, lt(calldatasize, 0x04)) + shr(0xe0, calldataload(0x00)) + dup1 + 0x26121ff0 + eq + tag_3 + jumpi + tag_2: + 0x00 + dup1 + revert + /* "optimize_full_storage_write/input.sol":111:211 function f() public {... */ + tag_3: + tag_4 + /* "optimize_full_storage_write/input.sol":192:207 nums[3] = 44444 */ + 0xad9c000000000000823500000000000056ce0000000000002b67 + /* "optimize_full_storage_write/input.sol":135:139 nums */ + 0x00 + /* "optimize_full_storage_write/input.sol":192:207 nums[3] = 44444 */ + sstore + /* "optimize_full_storage_write/input.sol":111:211 function f() public {... */ + jump + tag_4: + stop + + auxdata: +} diff --git a/test/formal/move_and_inside_or.py b/test/formal/move_and_inside_or.py new file mode 100644 index 000000000..09dc35c51 --- /dev/null +++ b/test/formal/move_and_inside_or.py @@ -0,0 +1,32 @@ +from rule import Rule +from opcodes import * + +""" +Rule: +AND(OR(AND(X, A), Y), B) -> OR(AND(X, A & B), AND(Y, B)) +""" + +rule = Rule() + +# bit width is irrelevant +n_bits = 128 + +# Input vars +X = BitVec('X', n_bits) +Y = BitVec('Y', n_bits) +A = BitVec('A', n_bits) +B = BitVec('B', n_bits) + +# Non optimized result, explicit form +nonopt = AND(OR(AND(X, A), Y), B) + +# Optimized result +opt = OR(AND(X, A & B), AND(Y, B)) + +rule.check(nonopt, opt) + +# Now the forms as they are constructod in the code. +for inner in [AND(X, A), AND(A, X)]: + for second in [OR(inner, Y), OR(Y, inner)]: + rule.check(AND(second, B), opt) + rule.check(AND(B, second), opt) diff --git a/test/libyul/yulOptimizerTests/fullSuite/and_or_combination.yul b/test/libyul/yulOptimizerTests/fullSuite/and_or_combination.yul new file mode 100644 index 000000000..b5f456054 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/and_or_combination.yul @@ -0,0 +1,25 @@ +{ + // Tests that masks that "add" up to + // the full bit width are removed. + let a := sload(0) + let x := sload(a) + let mask := 0xffffffffffffffff + x := or(and(x, not(mask)), 0x2b67) + mask := shl(64, mask) + x := or(and(x, not(mask)), 0x56ce0000000000000000) + mask := shl(64, mask) + x := or(and(x, not(mask)), shl(0x80, 0x8235)) + mask := shl(64, mask) + x := or(and(x, not(mask)), shl(0xc2, 0x2b67)) + sstore(a, x) +} +// ==== +// EVMVersion: >byzantium +// ---- +// step: fullSuite +// +// { +// { +// sstore(sload(0), 0xad9c000000000000823500000000000056ce0000000000002b67) +// } +// }