Move AND with constant inside OR.

This commit is contained in:
chriseth 2021-03-04 10:23:59 +01:00
parent 1d95f95635
commit 40c27ccc22
7 changed files with 152 additions and 0 deletions

View File

@ -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:

View File

@ -507,6 +507,23 @@ std::vector<SimplificationRule<Pattern>> 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))),

View File

@ -0,0 +1 @@
--optimize --asm --metadata-hash none

View File

@ -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;
}
}

View File

@ -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: <AUXDATA REMOVED>
}

View File

@ -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)

View File

@ -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)
// }
// }