mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Tests for UnusedReturnParameterPruner
This commit is contained in:
parent
4553b8c0d5
commit
03cbc5f6e7
@ -2,6 +2,7 @@
|
||||
|
||||
Compiler Features:
|
||||
* SMTChecker: Support named arguments in function calls.
|
||||
* Yul Optimizer: Prune return parameters of functions that are unused at callsite.
|
||||
|
||||
|
||||
### 0.7.5 (2020-11-18)
|
||||
|
@ -43,7 +43,7 @@ object "object" {
|
||||
x_7 := x_11
|
||||
}
|
||||
{
|
||||
let _5, _6, _7, _8 := iszero_169_788(_1, _1, _1, lt_171(x_4, x_5, x_6, x_7, _1, _1, _1, 10))
|
||||
let _5, _6, _7, _8 := iszero_169_864(_1, _1, _1, lt_171(x_4, x_5, x_6, x_7, _1, _1, _1, 10))
|
||||
if i32.eqz(i64.eqz(i64.or(i64.or(_5, _6), i64.or(_7, _8)))) { break }
|
||||
if i32.eqz(i64.eqz(i64.or(_3, i64.or(_1, eq(x_4, x_5, x_6, x_7, _1, _1, _1, 2))))) { break }
|
||||
if i32.eqz(i64.eqz(i64.or(_3, i64.or(_1, eq(x_4, x_5, x_6, x_7, _1, _1, _1, 4))))) { continue }
|
||||
@ -67,7 +67,7 @@ object "object" {
|
||||
let r1_1, carry_2 := add_carry(x1, y1, carry_1)
|
||||
r1 := r1_1
|
||||
}
|
||||
function iszero_169_788(x1, x2, x3, x4) -> r1, r2, r3, r4
|
||||
function iszero_169_864(x1, x2, x3, x4) -> r1, r2, r3, r4
|
||||
{
|
||||
r4 := i64.extend_i32_u(i64.eqz(i64.or(i64.or(x1, x2), i64.or(x3, x4))))
|
||||
}
|
||||
@ -206,7 +206,7 @@ Text representation:
|
||||
(br_if $label__3 (i32.eqz (i32.eqz (local.get $_4))))
|
||||
(block $label__4
|
||||
(block
|
||||
(local.set $_5 (call $iszero_169_788 (local.get $_1) (local.get $_1) (local.get $_1) (call $lt_171 (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 10))))
|
||||
(local.set $_5 (call $iszero_169_864 (local.get $_1) (local.get $_1) (local.get $_1) (call $lt_171 (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 10))))
|
||||
(local.set $_6 (global.get $global_))
|
||||
(local.set $_7 (global.get $global__1))
|
||||
(local.set $_8 (global.get $global__2))
|
||||
@ -310,7 +310,7 @@ Text representation:
|
||||
(local.get $r1)
|
||||
)
|
||||
|
||||
(func $iszero_169_788
|
||||
(func $iszero_169_864
|
||||
(param $x1 i64)
|
||||
(param $x2 i64)
|
||||
(param $x3 i64)
|
||||
|
@ -1396,11 +1396,11 @@ BOOST_AUTO_TEST_CASE(use_stack_optimization)
|
||||
BOOST_REQUIRE(contract["evm"]["bytecode"]["object"].isString());
|
||||
BOOST_CHECK(contract["evm"]["bytecode"]["object"].asString().length() > 20);
|
||||
|
||||
// Now disable stack optimizations and UnusedFunctionParameterPruner (p)
|
||||
// results in "stack too deep"
|
||||
// Now disable stack optimizations, UnusedFunctionParameterPruner (p) and
|
||||
// UnusedFunctionReturnParameterPruner (P). Results in "stack too deep"
|
||||
string optimiserSteps = OptimiserSettings::DefaultYulOptimiserSteps;
|
||||
optimiserSteps.erase(
|
||||
remove_if(optimiserSteps.begin(), optimiserSteps.end(), [](char ch) { return ch == 'p'; }),
|
||||
remove_if(optimiserSteps.begin(), optimiserSteps.end(), [](char ch) { return ch == 'p' || ch == 'P'; }),
|
||||
optimiserSteps.end()
|
||||
);
|
||||
parsedInput["settings"]["optimizer"]["details"]["yulDetails"]["stackAllocation"] = false;
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include <libyul/optimiser/Rematerialiser.h>
|
||||
#include <libyul/optimiser/ExpressionSimplifier.h>
|
||||
#include <libyul/optimiser/UnusedFunctionParameterPruner.h>
|
||||
#include <libyul/optimiser/UnusedFunctionReturnParameterPruner.h>
|
||||
#include <libyul/optimiser/UnusedPruner.h>
|
||||
#include <libyul/optimiser/ExpressionJoiner.h>
|
||||
#include <libyul/optimiser/OptimiserStep.h>
|
||||
@ -264,6 +265,12 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
|
||||
LiteralRematerialiser::run(*m_context, *m_object->code);
|
||||
UnusedFunctionParameterPruner::run(*m_context, *m_object->code);
|
||||
}
|
||||
else if (m_optimizerStep == "unusedFunctionReturnParameterPruner")
|
||||
{
|
||||
disambiguate();
|
||||
FunctionHoister::run(*m_context, *m_object->code);
|
||||
UnusedFunctionReturnParameterPruner::run(*m_context, *m_object->code);
|
||||
}
|
||||
else if (m_optimizerStep == "unusedPruner")
|
||||
{
|
||||
disambiguate();
|
||||
|
@ -1085,7 +1085,7 @@
|
||||
// sstore(x1, x0)
|
||||
// sstore(x3, x2)
|
||||
// sstore(1, x4)
|
||||
// pop(abi_encode_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint(mload(30), mload(31), mload(32), mload(33), mload(34), mload(35), mload(36), mload(37), mload(38), mload(39), mload(40), mload(41)))
|
||||
// abi_encode_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint_461(mload(30), mload(31), mload(32), mload(33), mload(34), mload(35), mload(36), mload(37), mload(38), mload(39), mload(40), mload(41))
|
||||
// }
|
||||
// function abi_decode_addresst_uint256t_bytes_calldatat_enum$_Operation(headStart, dataEnd) -> value0, value1, value2, value3, value4
|
||||
// {
|
||||
@ -1106,9 +1106,8 @@
|
||||
// if iszero(lt(_3, 3)) { revert(value4, value4) }
|
||||
// value4 := _3
|
||||
// }
|
||||
// function abi_encode_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint(headStart, value10, value9, value8, value7, value6, value5, value4, value3, value2, value1, value0) -> tail
|
||||
// function abi_encode_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint_461(headStart, value10, value9, value8, value7, value6, value5, value4, value3, value2, value1, value0)
|
||||
// {
|
||||
// tail := add(headStart, 352)
|
||||
// mstore(headStart, value0)
|
||||
// let _1 := sub(shl(160, 1), 1)
|
||||
// mstore(add(headStart, 32), and(value1, _1))
|
||||
|
@ -0,0 +1,34 @@
|
||||
// A test to see if loop condition is properly split
|
||||
{
|
||||
let b := f()
|
||||
// Return value of f is used in for loop condition. So f cannot be rewritten,
|
||||
// unless LoopConditionIntoBody and ExpressionSplitter is run.
|
||||
for {let a := 1} iszero(sub(f(), a)) {a := add(a, 1)}
|
||||
{}
|
||||
function f() -> x
|
||||
{
|
||||
x := sload(1)
|
||||
sstore(x, x)
|
||||
if calldataload(0) { leave }
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: fullSuite
|
||||
//
|
||||
// {
|
||||
// {
|
||||
// pop(f())
|
||||
// let a := 1
|
||||
// let a_1 := a
|
||||
// for { } true { a_1 := add(a_1, a) }
|
||||
// {
|
||||
// if iszero(iszero(sub(f(), a_1))) { break }
|
||||
// }
|
||||
// }
|
||||
// function f() -> x
|
||||
// {
|
||||
// x := sload(1)
|
||||
// sstore(x, x)
|
||||
// if calldataload(0) { leave }
|
||||
// }
|
||||
// }
|
@ -0,0 +1,30 @@
|
||||
// A test to see if UnusedFunctionReturnParameterPruner can deal with pop
|
||||
// Expression splitter converts `pop(f(a))` into
|
||||
// {
|
||||
// let z := f(a)
|
||||
// pop(z)
|
||||
// }
|
||||
// Unless `pop(z)` is removed, `f` cannot be rewritten.
|
||||
{
|
||||
let a := sload(1)
|
||||
pop(f(a))
|
||||
function f(x) -> y
|
||||
{
|
||||
if iszero(calldataload(0)) { leave }
|
||||
sstore(x, x)
|
||||
y := sload(x)
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: fullSuite
|
||||
//
|
||||
// {
|
||||
// { pop(f_17(sload(1))) }
|
||||
// function f(x)
|
||||
// {
|
||||
// if iszero(calldataload(0)) { leave }
|
||||
// sstore(x, x)
|
||||
// }
|
||||
// function f_17(x) -> y
|
||||
// { f(x) }
|
||||
// }
|
@ -0,0 +1,22 @@
|
||||
{
|
||||
let c, d := f(calldataload(0), calldataload(1))
|
||||
sstore(c, 1)
|
||||
function f(x, y) -> a, b
|
||||
{
|
||||
// to prevent the function getting inlined
|
||||
if iszero(calldataload(0)) {leave}
|
||||
a := sload(x)
|
||||
b := sload(y)
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: fullSuite
|
||||
//
|
||||
// {
|
||||
// { sstore(f(calldataload(0)), 1) }
|
||||
// function f(x) -> a
|
||||
// {
|
||||
// if iszero(calldataload(a)) { leave }
|
||||
// a := sload(x)
|
||||
// }
|
||||
// }
|
@ -0,0 +1,30 @@
|
||||
{
|
||||
// second return paramter is unused
|
||||
let j, k, l := f(1, 2, 3)
|
||||
sstore(0, j)
|
||||
sstore(1, l)
|
||||
function f(a, b, c) -> x, y, z
|
||||
{
|
||||
// second return parameter is unused
|
||||
let x_1, y_1, z_1 := f(1, 2, 3)
|
||||
x := x_1
|
||||
z := z_1
|
||||
x := add(x, 1)
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: fullSuite
|
||||
//
|
||||
// {
|
||||
// {
|
||||
// let x, z := f()
|
||||
// sstore(0, x)
|
||||
// sstore(1, z)
|
||||
// }
|
||||
// function f() -> x, z
|
||||
// {
|
||||
// let x_1, z_1 := f()
|
||||
// z := z_1
|
||||
// x := add(x_1, 1)
|
||||
// }
|
||||
// }
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
let a, b, c := f(sload(0))
|
||||
function f(d) -> x, y, z
|
||||
{
|
||||
y := mload(d)
|
||||
z := mload(2)
|
||||
sstore(y, z)
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: unusedFunctionReturnParameterPruner
|
||||
//
|
||||
// {
|
||||
// let a, b, c := f_1(sload(0))
|
||||
// function f(d)
|
||||
// {
|
||||
// let x
|
||||
// let y
|
||||
// let z
|
||||
// y := mload(d)
|
||||
// z := mload(2)
|
||||
// sstore(y, z)
|
||||
// }
|
||||
// function f_1(d_2) -> x_3, y_4, z_5
|
||||
// { f(d_2) }
|
||||
// }
|
@ -0,0 +1,30 @@
|
||||
// A test where the function should not be rewritten, since each parameter is used eventually.
|
||||
{
|
||||
let a, b, c := f(sload(0))
|
||||
sstore(a, 0)
|
||||
let a1, b1, c1 := f(sload(2))
|
||||
sstore(b1, 0)
|
||||
let a2, b2, c3 := f(sload(3))
|
||||
sstore(c3, 0)
|
||||
function f(d) -> x, y, z
|
||||
{
|
||||
y := mload(d)
|
||||
z := mload(2)
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: unusedFunctionReturnParameterPruner
|
||||
//
|
||||
// {
|
||||
// let a, b, c := f(sload(0))
|
||||
// sstore(a, 0)
|
||||
// let a1, b1, c1 := f(sload(2))
|
||||
// sstore(b1, 0)
|
||||
// let a2, b2, c3 := f(sload(3))
|
||||
// sstore(c3, 0)
|
||||
// function f(d) -> x, y, z
|
||||
// {
|
||||
// y := mload(d)
|
||||
// z := mload(2)
|
||||
// }
|
||||
// }
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
// b and c are unused
|
||||
let a, b, c := f(sload(0))
|
||||
sstore(a, 0)
|
||||
function f(d) -> x, y, z
|
||||
{
|
||||
y := mload(d)
|
||||
z := mload(2)
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: unusedFunctionReturnParameterPruner
|
||||
//
|
||||
// {
|
||||
// let a, b, c := f_1(sload(0))
|
||||
// sstore(a, 0)
|
||||
// function f(d) -> x
|
||||
// {
|
||||
// let y
|
||||
// let z
|
||||
// y := mload(d)
|
||||
// z := mload(2)
|
||||
// }
|
||||
// function f_1(d_2) -> x_3, y_4, z_5
|
||||
// { x_3 := f(d_2) }
|
||||
// }
|
@ -0,0 +1,32 @@
|
||||
// A test to see if the optimization step can deal with pop
|
||||
// Expression splitter converts `pop(f(a))` into
|
||||
// {
|
||||
// let z := f(a)
|
||||
// pop(z)
|
||||
// }
|
||||
// Unless `pop(z)` is removed, `f` cannot be rewritten.
|
||||
{
|
||||
let a := sload(1)
|
||||
let z := f(a)
|
||||
pop(z)
|
||||
function f(x) -> y
|
||||
{
|
||||
sstore(x, x)
|
||||
y := sload(x)
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: unusedFunctionReturnParameterPruner
|
||||
//
|
||||
// {
|
||||
// let a := sload(1)
|
||||
// let z := f_1(a)
|
||||
// function f(x)
|
||||
// {
|
||||
// let y
|
||||
// sstore(x, x)
|
||||
// y := sload(x)
|
||||
// }
|
||||
// function f_1(x_2) -> y_3
|
||||
// { f(x_2) }
|
||||
// }
|
@ -0,0 +1,20 @@
|
||||
// An example where the return parameter of the function should not be pruned
|
||||
{
|
||||
function f() -> y {
|
||||
y := sload(1)
|
||||
}
|
||||
|
||||
// return value is unused here
|
||||
let x := f()
|
||||
// return value is used here, so f cannot be pruned.
|
||||
sstore(1, f())
|
||||
}
|
||||
// ----
|
||||
// step: unusedFunctionReturnParameterPruner
|
||||
//
|
||||
// {
|
||||
// let x := f()
|
||||
// sstore(1, f())
|
||||
// function f() -> y
|
||||
// { y := sload(1) }
|
||||
// }
|
@ -0,0 +1,24 @@
|
||||
{
|
||||
let c, d := f(1, 2)
|
||||
sstore(c, 1)
|
||||
function f(x, y) -> a, b
|
||||
{
|
||||
a := sload(x)
|
||||
b := sload(y)
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: unusedFunctionReturnParameterPruner
|
||||
//
|
||||
// {
|
||||
// let c, d := f_1(1, 2)
|
||||
// sstore(c, 1)
|
||||
// function f(x, y) -> a
|
||||
// {
|
||||
// let b
|
||||
// a := sload(x)
|
||||
// b := sload(y)
|
||||
// }
|
||||
// function f_1(x_2, y_3) -> a_4, b_5
|
||||
// { a_4 := f(x_2, y_3) }
|
||||
// }
|
@ -0,0 +1,5 @@
|
||||
{ }
|
||||
// ----
|
||||
// step: unusedFunctionReturnParameterPruner
|
||||
//
|
||||
// { }
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
// The third and the last return value is unused
|
||||
let a, b, c, d, e := f(sload(0))
|
||||
sstore(a, b)
|
||||
let a1, b1, c1, d1, e1 := f(sload(1))
|
||||
sstore(a1, d1)
|
||||
let a2, b2, c2, d2, e2 := f(sload(2))
|
||||
sstore(d2, b2)
|
||||
function f(a_1) -> v, w, x, y, z
|
||||
{
|
||||
w := mload(a_1)
|
||||
y := mload(w)
|
||||
z := mload(y)
|
||||
sstore(y, z)
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: unusedFunctionReturnParameterPruner
|
||||
//
|
||||
// {
|
||||
// let a, b, c, d, e := f_1(sload(0))
|
||||
// sstore(a, b)
|
||||
// let a1, b1, c1, d1, e1 := f_1(sload(1))
|
||||
// sstore(a1, d1)
|
||||
// let a2, b2, c2, d2, e2 := f_1(sload(2))
|
||||
// sstore(d2, b2)
|
||||
// function f(a_1) -> v, w, y
|
||||
// {
|
||||
// let x
|
||||
// let z
|
||||
// w := mload(a_1)
|
||||
// y := mload(w)
|
||||
// z := mload(y)
|
||||
// sstore(y, z)
|
||||
// }
|
||||
// function f_1(a_1_2) -> v_3, w_4, x_5, y_6, z_7
|
||||
// { v_3, w_4, y_6 := f(a_1_2) }
|
||||
// }
|
@ -0,0 +1,22 @@
|
||||
{
|
||||
let a := f()
|
||||
// Return value of f is used here. So f cannot be rewritten.
|
||||
sstore(sload(f()), 2)
|
||||
function f() -> x
|
||||
{
|
||||
x := sload(1)
|
||||
sstore(x, x)
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: unusedFunctionReturnParameterPruner
|
||||
//
|
||||
// {
|
||||
// let a := f()
|
||||
// sstore(sload(f()), 2)
|
||||
// function f() -> x
|
||||
// {
|
||||
// x := sload(1)
|
||||
// sstore(x, x)
|
||||
// }
|
||||
// }
|
@ -0,0 +1,25 @@
|
||||
// A test to see if loop condition is properly split
|
||||
{
|
||||
let b := f()
|
||||
// Return value of f is used in for loop condition. So f cannot be rewritten.
|
||||
for {let a := 1} iszero(sub(f(), a)) {a := add(a, 1)}
|
||||
{}
|
||||
function f() -> x
|
||||
{
|
||||
x := sload(1)
|
||||
sstore(x, x)
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: unusedFunctionReturnParameterPruner
|
||||
//
|
||||
// {
|
||||
// let b := f()
|
||||
// for { let a := 1 } iszero(sub(f(), a)) { a := add(a, 1) }
|
||||
// { }
|
||||
// function f() -> x
|
||||
// {
|
||||
// x := sload(1)
|
||||
// sstore(x, x)
|
||||
// }
|
||||
// }
|
15
test/libyul/yulOptimizerTests/unusedPruner/pop_function.yul
Normal file
15
test/libyul/yulOptimizerTests/unusedPruner/pop_function.yul
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
let a := f()
|
||||
pop(a)
|
||||
function f() -> y {
|
||||
sstore(1, sload(1))
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// step: unusedPruner
|
||||
//
|
||||
// {
|
||||
// pop(f())
|
||||
// function f() -> y
|
||||
// { sstore(1, sload(1)) }
|
||||
// }
|
Loading…
Reference in New Issue
Block a user