diff --git a/libyul/optimiser/ValueConstraintBasedSimplifier.h b/libyul/optimiser/ValueConstraintBasedSimplifier.h index 9f48e5ee8..2889bc7da 100644 --- a/libyul/optimiser/ValueConstraintBasedSimplifier.h +++ b/libyul/optimiser/ValueConstraintBasedSimplifier.h @@ -96,7 +96,6 @@ protected: void valuesCleared(std::set const& _names) override; -private: explicit ValueConstraintBasedSimplifier(Dialect const& _dialect): DataFlowAnalyzer(_dialect) {} std::map m_variableConstraints; diff --git a/test/libyul/VerboseValueConstraintsSimplifier.cpp b/test/libyul/VerboseValueConstraintsSimplifier.cpp new file mode 100644 index 000000000..993f9e0be --- /dev/null +++ b/test/libyul/VerboseValueConstraintsSimplifier.cpp @@ -0,0 +1,60 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Debugging / testing component that adds reporting to the value constraint based + * optimiser stage. + */ + +#include + +#include +#include + +using namespace std; +using namespace dev; +using namespace yul; +using namespace yul::test; + + +void VerboseValueConstraintsSimplifier::run(Dialect const& _dialect, Block& _ast, string& _report) +{ + VerboseValueConstraintsSimplifier{_dialect, _report}(_ast); +} + +void VerboseValueConstraintsSimplifier::handleAssignment(set const& _names, Expression* _value) +{ + ValueConstraintBasedSimplifier::handleAssignment(_names, _value); + + for (YulString var: _names) + { + auto it = m_variableConstraints.find(var); + if (it == m_variableConstraints.end()) + continue; + m_report += + var.str() + + ":\n"; + ValueConstraint const& constr = it->second; + if (boost::optional value = constr.isConstant()) + m_report += " = " + formatNumberReadable(*value) + "\n"; + else + m_report += + (" min: " + formatNumberReadable(it->second.minValue) + "\n") + + (" max: " + formatNumberReadable(it->second.maxValue) + "\n") + + (" minB: " + formatNumberReadable(it->second.minBits) + "\n") + + (" maxB: " + formatNumberReadable(it->second.maxBits) + "\n"); + } +} diff --git a/test/libyul/VerboseValueConstraintsSimplifier.h b/test/libyul/VerboseValueConstraintsSimplifier.h new file mode 100644 index 000000000..092f30f62 --- /dev/null +++ b/test/libyul/VerboseValueConstraintsSimplifier.h @@ -0,0 +1,52 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Debugging / testing component that adds reporting to the value constraint based + * optimiser stage. + */ + +#pragma once + +#include + +namespace yul +{ +namespace test +{ + +/** + * Debugging / testing component that adds reporting to the value constraint based + * optimiser stage. + */ +class VerboseValueConstraintsSimplifier: public ValueConstraintBasedSimplifier +{ +public: + static void run(Dialect const& _dialect, Block& _ast, std::string& _report); + +protected: + VerboseValueConstraintsSimplifier(Dialect const& _dialect, std::string& _report): + ValueConstraintBasedSimplifier(_dialect), + m_report(_report) + {} + void handleAssignment(std::set const& _names, Expression* _value) override; + +private: + std::string& m_report; +}; + +} +} diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index d77279c29..aa6f78639 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -17,6 +17,8 @@ #include +#include + #include #include @@ -103,6 +105,8 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con if (!parse(_stream, _linePrefix, _formatted)) return false; + string additionalInfo; + if (m_optimizerStep == "disambiguator") disambiguate(); else if (m_optimizerStep == "blockFlattener") @@ -186,7 +190,9 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con else if (m_optimizerStep == "valueConstraintBasedSimplifier") { disambiguate(); - ValueConstraintBasedSimplifier::run(*m_dialect, *m_ast); + + // subclass of ValueConstraintBasedSimplifier that adds reporting + VerboseValueConstraintsSimplifier::run(*m_dialect, *m_ast, additionalInfo); } else if (m_optimizerStep == "fullSimplify") { @@ -265,7 +271,7 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con return false; } - m_obtainedResult = m_optimizerStep + "\n" + AsmPrinter{m_yul}(*m_ast) + "\n"; + m_obtainedResult = m_optimizerStep + "\n" + additionalInfo + AsmPrinter{m_yul}(*m_ast) + "\n"; if (m_expectation != m_obtainedResult) { diff --git a/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/arithmetics.yul b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/arithmetics.yul new file mode 100644 index 000000000..e0f81cb57 --- /dev/null +++ b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/arithmetics.yul @@ -0,0 +1,48 @@ +{ + let a := and(calldataload(0), 0xffff) + let b := and(calldataload(1), 0xffff) + let c := add(a, b) + let d := add(a, calldataload(7)) + let e := sub(add(a, 0x1000000), b) + let f := sub(add(a, 7), 1) +} +// ---- +// valueConstraintBasedSimplifier +// a: +// min: 0 +// max: 65535 +// minB: 0 +// maxB: 65535 +// b: +// min: 0 +// max: 65535 +// minB: 0 +// maxB: 65535 +// c: +// min: 0 +// max: 131070 +// minB: 0 +// maxB: 131071 +// d: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// e: +// min: 16711681 +// max: 0x0100ffff +// minB: 0 +// maxB: 0x02 * 2**24 - 1 +// f: +// min: 6 +// max: 65541 +// minB: 0 +// maxB: 131071 +// { +// let a := and(calldataload(0), 0xffff) +// let b := and(calldataload(1), 0xffff) +// let c := add(a, b) +// let d := add(a, calldataload(7)) +// let e := sub(add(a, 0x1000000), b) +// let f := sub(add(a, 7), 1) +// } diff --git a/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/bits.yul b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/bits.yul new file mode 100644 index 000000000..cf55cfbe2 --- /dev/null +++ b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/bits.yul @@ -0,0 +1,46 @@ +{ + let a := 0xffff + let b := or(shl(20, a), and(calldataload(0), 0xff)) + let c := shl(add(300, lt(a, b)), calldataload(0)) + let d := shr(add(300, lt(a, b)), calldataload(0)) + let e := and(b, 0xff) + let f := not(b) + let g := byte(calldataload(0), 2) +} +// ---- +// valueConstraintBasedSimplifier +// a: +// = 65535 +// b: +// min: 0 +// max: 0x10 * 2**32 - 1 +// minB: 0x0fFFF00000 +// maxB: 0x0fFFF000ff +// c: +// = 0 +// d: +// = 0 +// e: +// min: 0 +// max: 255 +// minB: 0 +// maxB: 255 +// f: +// min: 0 +// max: 2**256 - 1 +// minB: 0xFFFFffffFFFFffffFFFFffffFFFFffffFFFFffffFFFFffffFFFFfff0000Fff00 +// maxB: 0xFFFFffffFFFFffffFFFFffffFFFFffffFFFFffffFFFFffffFFFFfff0000Fffff +// g: +// min: 0 +// max: 255 +// minB: 0 +// maxB: 255 +// { +// let a := 0xffff +// let b := or(0x0ffff00000, and(calldataload(0), 0xff)) +// let c := 0 +// let d := 0 +// let e := and(b, 0xff) +// let f := not(b) +// let g := byte(calldataload(0), 2) +// } diff --git a/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/chainaccess.yul b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/chainaccess.yul new file mode 100644 index 000000000..dae7e42c0 --- /dev/null +++ b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/chainaccess.yul @@ -0,0 +1,205 @@ +{ + // restricted to 160 bits + let a := address() + let b := origin() + let c := caller() + let d := coinbase() + let e := create(0, 0, 0) + let f := create2(0, 0, 0, 0) + // restricted to bool + let gb := call(0, 0, 0, 0, 0, 0, 0) + let hb := callcode(0, 0, 0, 0, 0, 0, 0) + let ib := delegatecall(0, 0, 0, 0, 0, 0) + let jb := staticcall(0, 0, 0, 0, 0, 0) + // No restriction starting from here + let k_ := keccak256(0, 0) + let l_ := extcodehash(0) + let m_ := blockhash(0) + let n_ := mload(0) + let o_ := sload(0) + let p_ := balance(0) + let q_ := callvalue() + let r_ := calldatasize() + let s_ := gasprice() + let t_ := extcodesize(0) + let u_ := returndatasize() + let v_ := timestamp() + let w_ := number() + let x_ := difficulty() + let y_ := gaslimit() + let z_ := pc() + let z1_ := msize() + let z2_ := gas() +} +// ---- +// valueConstraintBasedSimplifier +// a: +// min: 0 +// max: 2**160 - 1 +// minB: 0 +// maxB: 2**160 - 1 +// b: +// min: 0 +// max: 2**160 - 1 +// minB: 0 +// maxB: 2**160 - 1 +// c: +// min: 0 +// max: 2**160 - 1 +// minB: 0 +// maxB: 2**160 - 1 +// d: +// min: 0 +// max: 2**160 - 1 +// minB: 0 +// maxB: 2**160 - 1 +// e: +// min: 0 +// max: 2**160 - 1 +// minB: 0 +// maxB: 2**160 - 1 +// f: +// min: 0 +// max: 2**160 - 1 +// minB: 0 +// maxB: 2**160 - 1 +// gb: +// min: 0 +// max: 1 +// minB: 0 +// maxB: 1 +// hb: +// min: 0 +// max: 1 +// minB: 0 +// maxB: 1 +// ib: +// min: 0 +// max: 1 +// minB: 0 +// maxB: 1 +// jb: +// min: 0 +// max: 1 +// minB: 0 +// maxB: 1 +// k_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// l_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// m_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// n_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// o_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// p_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// q_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// r_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// s_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// t_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// u_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// v_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// w_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// x_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// y_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// z_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// z1_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// z2_: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// { +// let a := address() +// let b := origin() +// let c := caller() +// let d := coinbase() +// let e := create(0, 0, 0) +// let f := create2(0, 0, 0, 0) +// let gb := call(0, 0, 0, 0, 0, 0, 0) +// let hb := callcode(0, 0, 0, 0, 0, 0, 0) +// let ib := delegatecall(0, 0, 0, 0, 0, 0) +// let jb := staticcall(0, 0, 0, 0, 0, 0) +// let k_ := keccak256(0, 0) +// let l_ := extcodehash(0) +// let m_ := blockhash(0) +// let n_ := mload(0) +// let o_ := sload(0) +// let p_ := balance(0) +// let q_ := callvalue() +// let r_ := calldatasize() +// let s_ := gasprice() +// let t_ := extcodesize(0) +// let u_ := returndatasize() +// let v_ := timestamp() +// let w_ := number() +// let x_ := difficulty() +// let y_ := gaslimit() +// let z_ := pc() +// let z1_ := msize() +// let z2_ := gas() +// } diff --git a/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/comparison.yul b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/comparison.yul new file mode 100644 index 000000000..b3e192eec --- /dev/null +++ b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/comparison.yul @@ -0,0 +1,74 @@ +{ + let a := and(calldataload(0), 0xf) + let b := and(calldataload(1), 0xf) + let c := add(b, 2) // overlaps with a + let d := add(b, 0x100) // does not overlap with a + let e := lt(a, c) + let f := lt(a, d) + let g := lt(c, a) + let h := lt(d, a) + let r := lt(a, a) // currently unknown, might change later. + let x := gt(d, a) + let y := gt(c, a) +} +// ---- +// valueConstraintBasedSimplifier +// a: +// min: 0 +// max: 15 +// minB: 0 +// maxB: 15 +// b: +// min: 0 +// max: 15 +// minB: 0 +// maxB: 15 +// c: +// min: 2 +// max: 17 +// minB: 0 +// maxB: 31 +// d: +// min: 256 +// max: 271 +// minB: 0 +// maxB: 511 +// e: +// min: 0 +// max: 1 +// minB: 0 +// maxB: 1 +// f: +// = 1 +// g: +// min: 0 +// max: 1 +// minB: 0 +// maxB: 1 +// h: +// = 0 +// r: +// min: 0 +// max: 1 +// minB: 0 +// maxB: 1 +// x: +// = 1 +// y: +// min: 0 +// max: 1 +// minB: 0 +// maxB: 1 +// { +// let a := and(calldataload(0), 0xf) +// let b := and(calldataload(1), 0xf) +// let c := add(b, 2) +// let d := add(b, 0x100) +// let e := lt(a, c) +// let f := 1 +// let g := lt(c, a) +// let h := 0 +// let r := lt(a, a) +// let x := 1 +// let y := gt(c, a) +// } diff --git a/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/equal.yul b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/equal.yul new file mode 100644 index 000000000..0aa29f2ce --- /dev/null +++ b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/equal.yul @@ -0,0 +1,25 @@ +{ + let x := 7 + let a := eq(x, x) + let b := eq(x, and(calldataload(0), 1)) // not equal + let c := eq(calldataload(0), calldataload(1)) +} +// ---- +// valueConstraintBasedSimplifier +// x: +// = 7 +// a: +// = 1 +// b: +// = 0 +// c: +// min: 0 +// max: 1 +// minB: 0 +// maxB: 1 +// { +// let x := 7 +// let a := 1 +// let b := 0 +// let c := eq(calldataload(0), calldataload(1)) +// } diff --git a/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/lt.yul b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/lt.yul index 50f2d0728..4530732e8 100644 --- a/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/lt.yul +++ b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/lt.yul @@ -9,6 +9,8 @@ } // ---- // valueConstraintBasedSimplifier +// x: +// = 2 // { // let x := 2 // if 1 diff --git a/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/lt_bits_combined.yul b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/lt_bits_combined.yul index c4581d255..35e7f61ae 100644 --- a/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/lt_bits_combined.yul +++ b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/lt_bits_combined.yul @@ -5,6 +5,11 @@ } // ---- // valueConstraintBasedSimplifier +// x: +// min: 0 +// max: 255 +// minB: 0 +// maxB: 255 // { // let x := and(callvalue(), 0xff) // if 1 diff --git a/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/multi_assign.yul b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/multi_assign.yul new file mode 100644 index 000000000..5433b6772 --- /dev/null +++ b/test/libyul/yulOptimizerTests/valueConstraintBasedSimplifier/multi_assign.yul @@ -0,0 +1,54 @@ +{ + function f() -> x, y {} + let a, b := f() + let d := a + let e := b + a := 1 + b := 3 + a, b := f() + let s := a + let t := b +} +// ---- +// valueConstraintBasedSimplifier +// x: +// = 0 +// y: +// = 0 +// d: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// e: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// a: +// = 1 +// b: +// = 3 +// s: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// t: +// min: 0 +// max: 2**256 - 1 +// minB: 0 +// maxB: 2**256 - 1 +// { +// function f() -> x, y +// { +// } +// let a, b := f() +// let d := a +// let e := b +// a := 1 +// b := 3 +// a, b := f() +// let s := a +// let t := b +// } diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 2cc31170f..9b5e6f774 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable(isoltest ../libsolidity/ASTJSONTest.cpp ../libsolidity/SMTCheckerJSONTest.cpp ../libyul/ObjectCompilerTest.cpp + ../libyul/VerboseValueConstraintsSimplifier.cpp ../libyul/YulOptimizerTest.cpp ../libyul/YulInterpreterTest.cpp )