diff --git a/Changelog.md b/Changelog.md index 95a8a25e0..9b3639916 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Compiler Features: * SMTChecker: Support ``addmod`` and ``mulmod``. + * Optimizer: Optimize ``exp`` when base is -1. ### 0.7.2 (2020-09-28) diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h index 883de4c9f..8943c662b 100644 --- a/libevmasm/RuleList.h +++ b/libevmasm/RuleList.h @@ -643,6 +643,16 @@ std::vector> evmRuleList( Builtins::EXP(2, X), [=]() -> Pattern { return Builtins::SHL(X, 1); } ); + rules.emplace_back( + Builtins::EXP(Word(-1), X), + [=]() -> Pattern + { + return Builtins::SUB( + Builtins::ISZERO(Builtins::AND(X, Word(1))), + Builtins::AND(X, Word(1)) + ); + } + ); return rules; } diff --git a/test/formal/exp_neg_one.py b/test/formal/exp_neg_one.py new file mode 100644 index 000000000..88416496e --- /dev/null +++ b/test/formal/exp_neg_one.py @@ -0,0 +1,16 @@ +from rule import Rule +from opcodes import * +from util import * + +""" +Checking conversion of exp(-1, X) to sub(isZero(and(X, 1)), and(X, 1)) +""" + +rule = Rule() +n_bits = 256 + +X = BitVec('X', n_bits) + +exp_neg_one = If(MOD(X, 2) == 0, BitVecVal(1, n_bits), BVUnsignedMax(n_bits, n_bits)) + +rule.check(SUB(ISZERO(AND(X, 1)), AND(X, 1)), exp_neg_one) diff --git a/test/libsolidity/gasTests/exp.sol b/test/libsolidity/gasTests/exp.sol new file mode 100644 index 000000000..238af8990 --- /dev/null +++ b/test/libsolidity/gasTests/exp.sol @@ -0,0 +1,29 @@ +pragma experimental ABIEncoderV2; + +contract C { + function exp_neg_one(uint exponent) public returns(int) { + return (-1)**exponent; + } + function exp_two(uint exponent) public returns(uint) { + return 2**exponent; + } + function exp_zero(uint exponent) public returns(uint) { + return 0**exponent; + } + function exp_one(uint exponent) public returns(uint) { + return 1**exponent; + } +} +// ==== +// optimize: false +// optimize-yul: false +// ---- +// creation: +// codeDepositCost: 119800 +// executionCost: 165 +// totalCost: 119965 +// external: +// exp_neg_one(uint256): 2259 +// exp_one(uint256): infinite +// exp_two(uint256): infinite +// exp_zero(uint256): infinite diff --git a/test/libsolidity/gasTests/exp_optimized.sol b/test/libsolidity/gasTests/exp_optimized.sol new file mode 100644 index 000000000..499a3b5a5 --- /dev/null +++ b/test/libsolidity/gasTests/exp_optimized.sol @@ -0,0 +1,29 @@ +pragma experimental ABIEncoderV2; + +contract C { + function exp_neg_one(uint exponent) public returns(int) { + return (-1)**exponent; + } + function exp_two(uint exponent) public returns(uint) { + return 2**exponent; + } + function exp_zero(uint exponent) public returns(uint) { + return 0**exponent; + } + function exp_one(uint exponent) public returns(uint) { + return 1**exponent; + } +} +// ==== +// optimize: true +// optimize-yul: true +// ---- +// creation: +// codeDepositCost: 53200 +// executionCost: 105 +// totalCost: 53305 +// external: +// exp_neg_one(uint256): 1962 +// exp_one(uint256): 1915 +// exp_two(uint256): 1893 +// exp_zero(uint256): 1937 diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/exp_simplifications.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/exp_simplifications.yul index 879b05883..bfc1cbb2b 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/exp_simplifications.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/exp_simplifications.yul @@ -5,6 +5,7 @@ sstore(2, exp(2, t)) // The following should not be simplified sstore(3, exp(8, t)) + sstore(4, exp(115792089237316195423570985008687907853269984665640564039457584007913129639935, t)) } // ==== // EVMVersion: >=constantinople @@ -19,4 +20,5 @@ // let _8 := 2 // sstore(_8, shl(t, 1)) // sstore(3, exp(8, t)) +// sstore(4, sub(iszero(and(t, 1)), and(t, 1))) // }