Merge pull request #7353 from ethereum/develop

Update 0.6.0 from develop
This commit is contained in:
chriseth
2019-09-04 20:21:37 +02:00
committed by GitHub
62 changed files with 1331 additions and 661 deletions
+4
View File
@@ -75,6 +75,10 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::vm* _vm):
m_evmVersion = EVMC_BYZANTIUM;
else if (_evmVersion == langutil::EVMVersion::constantinople())
m_evmVersion = EVMC_CONSTANTINOPLE;
else if (_evmVersion == langutil::EVMVersion::istanbul())
assertThrow(false, Exception, "Istanbul is not supported yet.");
else if (_evmVersion == langutil::EVMVersion::berlin())
assertThrow(false, Exception, "Berlin is not supported yet.");
else //if (_evmVersion == langutil::EVMVersion::petersburg())
m_evmVersion = EVMC_PETERSBURG;
}
@@ -7,24 +7,16 @@
(local $_1 i64)
(local $pos i64)
(local $_2 i64)
(local $_3 i64)
(local $hi i64)
(local $_4 i64)
(local $y i64)
(local $hi_1 i64)
(local $hi_2 i64)
(local $hi_3 i64)
(local $hi_4 i64)
(set_local $_1 (i64.const 0))
(set_local $pos (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (i64.const 64)))
(set_local $_2 (i64.const 32))
(set_local $_3 (i64.shr_u (get_local $_1) (i64.const 16)))
(set_local $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (get_local $_1) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (get_local $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (get_local $_3))) (get_local $_2)))
(set_local $_4 (i64.shr_u (get_local $_1) (get_local $_2)))
(i64.store (get_local $pos) (i64.or (get_local $hi) (call $endian_swap_32 (get_local $_4)))) (set_local $hi_1 (i64.shl (call $endian_swap_16 (get_local $_1)) (i64.const 16)))
(set_local $hi_2 (i64.shl (i64.or (get_local $hi_1) (call $endian_swap_16 (get_local $_3))) (get_local $_2)))
(i64.store (i64.add (get_local $pos) (i64.const 8)) (i64.or (get_local $hi_2) (call $endian_swap_32 (get_local $_4)))) (set_local $hi_3 (i64.shl (call $endian_swap_32 (get_local $_1)) (get_local $_2)))
(i64.store (i64.add (get_local $pos) (i64.const 16)) (i64.or (get_local $hi_3) (call $endian_swap_32 (get_local $_4)))) (set_local $hi_4 (i64.shl (call $endian_swap_32 (i64.const 64)) (get_local $_2)))
(i64.store (i64.add (get_local $pos) (i64.const 24)) (i64.or (get_local $hi_4) (call $endian_swap_32 (i64.shr_u (i64.const 64) (get_local $_2))))) (call $eth.revert (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)))
(set_local $_2 (i64.const 65280))
(set_local $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (get_local $_1) (i64.const 8)) (get_local $_2)) (i64.and (i64.shr_u (get_local $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (get_local $_1) (i64.const 16)))) (i64.const 32)))
(set_local $y (i64.or (get_local $hi) (call $endian_swap_32 (i64.shr_u (get_local $_1) (i64.const 32)))))
(i64.store (get_local $pos) (get_local $y)) (i64.store (i64.add (get_local $pos) (i64.const 8)) (get_local $y)) (i64.store (i64.add (get_local $pos) (i64.const 16)) (get_local $y)) (set_local $hi_1 (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (i64.const 64) (i64.const 8)) (get_local $_2)) (i64.and (i64.shr_u (i64.const 64) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (i64.const 64) (i64.const 16)))) (i64.const 32)))
(i64.store (i64.add (get_local $pos) (i64.const 24)) (i64.or (get_local $hi_1) (call $endian_swap_32 (i64.shr_u (i64.const 64) (i64.const 32))))) (call $eth.revert (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)))
)
(func $u256_to_i32
@@ -74,44 +66,38 @@
(local $_1 i64)
(local $pos i64)
(local $hi i64)
(local $_2 i64)
(local $y i64)
(local $hi_1 i64)
(local $_3 i64)
(local $hi_2 i64)
(local $hi_3 i64)
(local $hi_4 i64)
(local $_2 i64)
(local $_3 i64)
(local $_4 i64)
(local $_5 i64)
(local $_6 i64)
(local $_7 i64)
(local $_8 i64)
(local $_9 i64)
(local $_10 i64)
(local $_11 i64)
(set_local $_1 (i64.const 0))
(set_local $pos (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (i64.const 64)))
(set_local $hi (i64.shl (call $endian_swap_16 (get_local $_1)) (i64.const 16)))
(set_local $_2 (i64.shr_u (get_local $_1) (i64.const 16)))
(set_local $hi_1 (i64.shl (i64.or (get_local $hi) (call $endian_swap_16 (get_local $_2))) (i64.const 32)))
(set_local $_3 (i64.shr_u (get_local $_1) (i64.const 32)))
(i64.store (get_local $pos) (i64.or (get_local $hi_1) (call $endian_swap_32 (get_local $_3)))) (set_local $hi_2 (i64.shl (call $endian_swap_16 (get_local $_1)) (i64.const 16)))
(set_local $hi_3 (i64.shl (i64.or (get_local $hi_2) (call $endian_swap_16 (get_local $_2))) (i64.const 32)))
(i64.store (i64.add (get_local $pos) (i64.const 8)) (i64.or (get_local $hi_3) (call $endian_swap_32 (get_local $_3)))) (set_local $hi_4 (i64.shl (call $endian_swap_32 (get_local $_1)) (i64.const 32)))
(i64.store (i64.add (get_local $pos) (i64.const 16)) (i64.or (get_local $hi_4) (call $endian_swap_32 (get_local $_3)))) (i64.store (i64.add (get_local $pos) (i64.const 24)) (call $endian_swap (i64.const 64))) (block
(set_local $_4 (datasize \"C_2_deployed\"))
(set_local $_5 (get_global $global_))
(set_local $_6 (get_global $global__1))
(set_local $_7 (get_global $global__2))
(set_local $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (get_local $_1) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (get_local $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (get_local $_1) (i64.const 16)))) (i64.const 32)))
(set_local $y (i64.or (get_local $hi) (call $endian_swap_32 (i64.shr_u (get_local $_1) (i64.const 32)))))
(i64.store (get_local $pos) (get_local $y)) (i64.store (i64.add (get_local $pos) (i64.const 8)) (get_local $y)) (i64.store (i64.add (get_local $pos) (i64.const 16)) (get_local $y)) (set_local $hi_1 (i64.shl (call $endian_swap_16 (i64.const 64)) (i64.const 16)))
(set_local $hi_2 (i64.shl (i64.or (get_local $hi_1) (call $endian_swap_16 (i64.shr_u (i64.const 64) (i64.const 16)))) (i64.const 32)))
(i64.store (i64.add (get_local $pos) (i64.const 24)) (i64.or (get_local $hi_2) (call $endian_swap_32 (i64.shr_u (i64.const 64) (i64.const 32))))) (block
(set_local $_2 (datasize \"C_2_deployed\"))
(set_local $_3 (get_global $global_))
(set_local $_4 (get_global $global__1))
(set_local $_5 (get_global $global__2))
)
(block
(set_local $_8 (dataoffset \"C_2_deployed\"))
(set_local $_9 (get_global $global_))
(set_local $_10 (get_global $global__1))
(set_local $_11 (get_global $global__2))
(set_local $_6 (dataoffset \"C_2_deployed\"))
(set_local $_7 (get_global $global_))
(set_local $_8 (get_global $global__1))
(set_local $_9 (get_global $global__2))
)
(call $eth.codeCopy (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_8) (get_local $_9) (get_local $_10) (get_local $_11)) (call $u256_to_i32 (get_local $_4) (get_local $_5) (get_local $_6) (get_local $_7))) (call $eth.finish (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_4) (get_local $_5) (get_local $_6) (get_local $_7)))
(call $eth.codeCopy (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_6) (get_local $_7) (get_local $_8) (get_local $_9)) (call $u256_to_i32 (get_local $_2) (get_local $_3) (get_local $_4) (get_local $_5))) (call $eth.finish (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_2) (get_local $_3) (get_local $_4) (get_local $_5)))
)
(func $u256_to_i32
@@ -147,16 +133,6 @@
(get_local $y)
)
(func $endian_swap
(param $x i64)
(result i64)
(local $y i64)
(local $hi i64)
(set_local $hi (i64.shl (call $endian_swap_32 (get_local $x)) (i64.const 32)))
(set_local $y (i64.or (get_local $hi) (call $endian_swap_32 (i64.shr_u (get_local $x) (i64.const 32)))))
(get_local $y)
)
)
"}}}},"errors":[{"component":"general","formattedMessage":"Warning: The Yul optimiser is still experimental. Do not use it in production unless correctness of generated code is verified with extensive tests.
","message":"The Yul optimiser is still experimental. Do not use it in production unless correctness of generated code is verified with extensive tests.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}}
+23 -23
View File
@@ -9,29 +9,6 @@ contract test {
// :test
// [
// {
// "constant": false,
// "inputs":
// [
// {
// "internalType": "uint256",
// "name": "a",
// "type": "uint256"
// }
// ],
// "name": "f",
// "outputs":
// [
// {
// "internalType": "uint256",
// "name": "d",
// "type": "uint256"
// }
// ],
// "payable": false,
// "stateMutability": "nonpayable",
// "type": "function"
// },
// {
// "anonymous": false,
// "inputs":
// [
@@ -76,5 +53,28 @@ contract test {
// "inputs": [],
// "name": "e3",
// "type": "event"
// },
// {
// "constant": false,
// "inputs":
// [
// {
// "internalType": "uint256",
// "name": "a",
// "type": "uint256"
// }
// ],
// "name": "f",
// "outputs":
// [
// {
// "internalType": "uint256",
// "name": "d",
// "type": "uint256"
// }
// ],
// "payable": false,
// "stateMutability": "nonpayable",
// "type": "function"
// }
// ]
+35 -35
View File
@@ -10,6 +10,20 @@ contract Derived is Base {
// :Base
// [
// {
// "anonymous": false,
// "inputs":
// [
// {
// "indexed": true,
// "internalType": "bytes32",
// "name": "evtArgBase",
// "type": "bytes32"
// }
// ],
// "name": "baseEvent",
// "type": "event"
// },
// {
// "constant": false,
// "inputs":
// [
@@ -31,7 +45,12 @@ contract Derived is Base {
// "payable": false,
// "stateMutability": "nonpayable",
// "type": "function"
// },
// }
// ]
//
//
// :Derived
// [
// {
// "anonymous": false,
// "inputs":
@@ -45,12 +64,21 @@ contract Derived is Base {
// ],
// "name": "baseEvent",
// "type": "event"
// }
// ]
//
//
// :Derived
// [
// },
// {
// "anonymous": false,
// "inputs":
// [
// {
// "indexed": true,
// "internalType": "uint256",
// "name": "evtArgDerived",
// "type": "uint256"
// }
// ],
// "name": "derivedEvent",
// "type": "event"
// },
// {
// "constant": false,
// "inputs":
@@ -96,33 +124,5 @@ contract Derived is Base {
// "payable": false,
// "stateMutability": "nonpayable",
// "type": "function"
// },
// {
// "anonymous": false,
// "inputs":
// [
// {
// "indexed": true,
// "internalType": "uint256",
// "name": "evtArgDerived",
// "type": "uint256"
// }
// ],
// "name": "derivedEvent",
// "type": "event"
// },
// {
// "anonymous": false,
// "inputs":
// [
// {
// "indexed": true,
// "internalType": "bytes32",
// "name": "evtArgBase",
// "type": "bytes32"
// }
// ],
// "name": "baseEvent",
// "type": "event"
// }
// ]
+23 -23
View File
@@ -6,6 +6,29 @@ contract test {
// :test
// [
// {
// "constant": true,
// "inputs":
// [
// {
// "internalType": "uint32",
// "name": "a",
// "type": "uint32"
// }
// ],
// "name": "boo",
// "outputs":
// [
// {
// "internalType": "uint256",
// "name": "b",
// "type": "uint256"
// }
// ],
// "payable": false,
// "stateMutability": "pure",
// "type": "function"
// },
// {
// "constant": false,
// "inputs":
// [
@@ -32,28 +55,5 @@ contract test {
// "payable": false,
// "stateMutability": "nonpayable",
// "type": "function"
// },
// {
// "constant": true,
// "inputs":
// [
// {
// "internalType": "uint32",
// "name": "a",
// "type": "uint32"
// }
// ],
// "name": "boo",
// "outputs":
// [
// {
// "internalType": "uint256",
// "name": "b",
// "type": "uint256"
// }
// ],
// "payable": false,
// "stateMutability": "pure",
// "type": "function"
// }
// ]
@@ -11,6 +11,19 @@ contract test {
// :test
// [
// {
// "inputs":
// [
// {
// "internalType": "enum test.ActionChoices",
// "name": "param",
// "type": "uint8"
// }
// ],
// "payable": false,
// "stateMutability": "nonpayable",
// "type": "constructor"
// },
// {
// "constant": false,
// "inputs": [],
// "name": "ret",
@@ -25,18 +38,5 @@ contract test {
// "payable": false,
// "stateMutability": "nonpayable",
// "type": "function"
// },
// {
// "inputs":
// [
// {
// "internalType": "enum test.ActionChoices",
// "name": "param",
// "type": "uint8"
// }
// ],
// "payable": false,
// "stateMutability": "nonpayable",
// "type": "constructor"
// }
// ]
+23 -23
View File
@@ -6,6 +6,29 @@ contract test {
// :test
// [
// {
// "constant": true,
// "inputs":
// [
// {
// "internalType": "uint32",
// "name": "a",
// "type": "uint32"
// }
// ],
// "name": "boo",
// "outputs":
// [
// {
// "internalType": "uint256",
// "name": "b",
// "type": "uint256"
// }
// ],
// "payable": false,
// "stateMutability": "view",
// "type": "function"
// },
// {
// "constant": false,
// "inputs":
// [
@@ -32,28 +55,5 @@ contract test {
// "payable": false,
// "stateMutability": "nonpayable",
// "type": "function"
// },
// {
// "constant": true,
// "inputs":
// [
// {
// "internalType": "uint32",
// "name": "a",
// "type": "uint32"
// }
// ],
// "name": "boo",
// "outputs":
// [
// {
// "internalType": "uint256",
// "name": "b",
// "type": "uint256"
// }
// ],
// "payable": false,
// "stateMutability": "view",
// "type": "function"
// }
// ]
+2 -2
View File
@@ -212,7 +212,7 @@ BOOST_AUTO_TEST_CASE(compound_assignment_division)
uint[] array;
function f(uint x, uint p) public {
require(x == 2);
require(array[p] == 10);
array[p] = 10;
array[p] /= array[p] / x;
assert(array[p] == x);
assert(array[p] == 0);
@@ -225,7 +225,7 @@ BOOST_AUTO_TEST_CASE(compound_assignment_division)
mapping (uint => uint) map;
function f(uint x, uint p) public {
require(x == 2);
require(map[p] == 10);
map[p] = 10;
map[p] /= map[p] / x;
assert(map[p] == x);
assert(map[p] == 0);
@@ -5,11 +5,11 @@ contract C
uint[] array;
function f(uint x, uint p) public {
require(x < 100);
require(array[p] == 100);
array[p] = 100;
array[p] += array[p] + x;
assert(array[p] < 300);
assert(array[p] < 110);
}
}
// ----
// Warning: (202-224): Assertion violation happens here
// Warning: (192-214): Assertion violation happens here
@@ -5,11 +5,11 @@ contract C
mapping (uint => uint) map;
function f(uint x, uint p) public {
require(x < 100);
require(map[p] == 100);
map[p] = 100;
map[p] += map[p] + x;
assert(map[p] < 300);
assert(map[p] < 110);
}
}
// ----
// Warning: (208-228): Assertion violation happens here
// Warning: (198-218): Assertion violation happens here
@@ -5,11 +5,11 @@ contract C
uint[] array;
function f(uint x, uint p) public {
require(x < 10);
require(array[p] == 10);
array[p] = 10;
array[p] *= array[p] + x;
assert(array[p] <= 190);
assert(array[p] < 50);
}
}
// ----
// Warning: (201-222): Assertion violation happens here
// Warning: (191-212): Assertion violation happens here
@@ -5,11 +5,11 @@ contract C
mapping (uint => uint) map;
function f(uint x, uint p) public {
require(x < 10);
require(map[p] == 10);
map[p] = 10;
map[p] *= map[p] + x;
assert(map[p] <= 190);
assert(map[p] < 50);
}
}
// ----
// Warning: (207-226): Assertion violation happens here
// Warning: (197-216): Assertion violation happens here
@@ -5,11 +5,11 @@ contract C
uint[] array;
function f(uint x, uint p) public {
require(x < 100);
require(array[p] == 200);
array[p] = 200;
array[p] -= array[p] - x;
assert(array[p] >= 0);
assert(array[p] < 90);
}
}
// ----
// Warning: (201-222): Assertion violation happens here
// Warning: (191-212): Assertion violation happens here
@@ -5,11 +5,11 @@ contract C
mapping (uint => uint) map;
function f(uint x, uint p) public {
require(x < 100);
require(map[p] == 200);
map[p] = 200;
map[p] -= map[p] - x;
assert(map[p] >= 0);
assert(map[p] < 90);
}
}
// ----
// Warning: (207-226): Assertion violation happens here
// Warning: (197-216): Assertion violation happens here
@@ -10,12 +10,9 @@ contract C
delete a;
else
delete a[2];
// Assertion fails as false positive because
// setZeroValue for arrays needs \forall i . a[i] = 0
// which is still unimplemented.
assert(a[2] == 0);
assert(a[1] == 0);
}
}
// ----
// Warning: (118-119): Condition is always true.
// Warning: (297-314): Assertion violation happens here
@@ -6,10 +6,6 @@ contract C
function f() public {
require(a[2][3] == 4);
delete a;
// Fails as false positive.
// setZeroValue needs forall for arrays.
assert(a[2][3] == 0);
}
}
// ----
// Warning: (194-214): Assertion violation happens here
@@ -9,11 +9,7 @@ contract C
delete a;
else
delete a[2];
// Fails as false positive since
// setZeroValue for arrays needs forall
// which is unimplemented.
assert(a[2][3] == 0);
assert(a[1][1] == 0);
}
}
// ----
// Warning: (266-286): Assertion violation happens here
@@ -16,12 +16,9 @@ contract C
g();
else
h();
// Assertion fails as false positive because
// setZeroValue for arrays needs \forall i . a[i] = 0
// which is still unimplemented.
assert(a[2] == 0);
assert(a[1] == 0);
}
}
// ----
// Warning: (201-202): Condition is always true.
// Warning: (367-384): Assertion violation happens here
@@ -4,10 +4,11 @@ contract C
{
uint[][] array;
function f(uint x, uint y, uint z, uint t) public view {
require(array[x][y] == 200);
// TODO change to = 200 when 2d assignments are supported.
require(array[x][y] < 200);
require(x == z && y == t);
assert(array[z][t] > 300);
}
}
// ----
// Warning: (183-208): Assertion violation happens here
// Warning: (243-268): Assertion violation happens here
@@ -4,10 +4,11 @@ contract C
{
uint[][][] array;
function f(uint x, uint y, uint z, uint t, uint w, uint v) public view {
require(array[x][y][z] == 200);
// TODO change to = 200 when 3d assignments are supported.
require(array[x][y][z] < 200);
require(x == t && y == w && z == v);
assert(array[t][w][v] > 300);
}
}
// ----
// Warning: (214-242): Assertion violation happens here
// Warning: (274-302): Assertion violation happens here
@@ -4,10 +4,10 @@ contract C
{
uint[10][20] array;
function f(uint x, uint y, uint z, uint t) public view {
require(array[x][y] == 200);
require(array[x][y] < 200);
require(x == z && y == t);
assert(array[z][t] > 300);
}
}
// ----
// Warning: (187-212): Assertion violation happens here
// Warning: (186-211): Assertion violation happens here
@@ -4,10 +4,11 @@ contract C
{
uint[10][20][30] array;
function f(uint x, uint y, uint z, uint t, uint w, uint v) public view {
require(array[x][y][z] == 200);
// TODO change to = 200 when 3d assignments are supported.
require(array[x][y][z] < 200);
require(x == t && y == w && z == v);
assert(array[t][w][v] > 300);
}
}
// ----
// Warning: (220-248): Assertion violation happens here
// Warning: (280-308): Assertion violation happens here
@@ -9,4 +9,3 @@ contract C
}
}
// ----
// Warning: (125-144): Assertion violation happens here
+10 -8
View File
@@ -26,6 +26,7 @@
#include <libyul/optimiser/ControlFlowSimplifier.h>
#include <libyul/optimiser/DeadCodeEliminator.h>
#include <libyul/optimiser/Disambiguator.h>
#include <libyul/optimiser/CallGraphGenerator.h>
#include <libyul/optimiser/CommonSubexpressionEliminator.h>
#include <libyul/optimiser/NameCollector.h>
#include <libyul/optimiser/EquivalentFunctionCombiner.h>
@@ -45,6 +46,7 @@
#include <libyul/optimiser/ExpressionJoiner.h>
#include <libyul/optimiser/SSAReverser.h>
#include <libyul/optimiser/SSATransform.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/RedundantAssignEliminator.h>
#include <libyul/optimiser/StructuralSimplifier.h>
#include <libyul/optimiser/StackCompressor.h>
@@ -149,7 +151,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
else if (m_optimizerStep == "commonSubexpressionEliminator")
{
disambiguate();
(CommonSubexpressionEliminator{*m_dialect})(*m_ast);
CommonSubexpressionEliminator::run(*m_dialect, *m_ast);
}
else if (m_optimizerStep == "expressionSplitter")
{
@@ -218,9 +220,9 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
NameDispenser nameDispenser{*m_dialect, *m_ast};
ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast);
ForLoopInitRewriter{}(*m_ast);
CommonSubexpressionEliminator{*m_dialect}(*m_ast);
CommonSubexpressionEliminator::run(*m_dialect, *m_ast);
ExpressionSimplifier::run(*m_dialect, *m_ast);
UnusedPruner::runUntilStabilised(*m_dialect, *m_ast);
UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast);
DeadCodeEliminator{*m_dialect}(*m_ast);
ExpressionJoiner::run(*m_ast);
ExpressionJoiner::run(*m_ast);
@@ -228,7 +230,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
else if (m_optimizerStep == "unusedPruner")
{
disambiguate();
UnusedPruner::runUntilStabilised(*m_dialect, *m_ast);
UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast);
}
else if (m_optimizerStep == "deadCodeEliminator")
{
@@ -260,12 +262,12 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
ForLoopInitRewriter{}(*m_ast);
NameDispenser nameDispenser{*m_dialect, *m_ast};
ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast);
CommonSubexpressionEliminator{*m_dialect}(*m_ast);
CommonSubexpressionEliminator::run(*m_dialect, *m_ast);
ExpressionSimplifier::run(*m_dialect, *m_ast);
LoadResolver::run(*m_dialect, *m_ast);
UnusedPruner::runUntilStabilised(*m_dialect, *m_ast);
UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast);
ExpressionJoiner::run(*m_ast);
ExpressionJoiner::run(*m_ast);
}
@@ -298,8 +300,8 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
RedundantAssignEliminator::run(*m_dialect, *m_ast);
// reverse SSA
SSAReverser::run(*m_ast);
CommonSubexpressionEliminator{*m_dialect}(*m_ast);
UnusedPruner::runUntilStabilised(*m_dialect, *m_ast);
CommonSubexpressionEliminator::run(*m_dialect, *m_ast);
UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast);
}
else if (m_optimizerStep == "stackCompressor")
{
@@ -0,0 +1,26 @@
{
function double(x) -> y { y := add(x, x) }
function double_with_se(x) -> y { y := add(x, x) mstore(40, 4) }
let i := mload(3)
let a := double(i)
let b := double(i)
let c := double_with_se(i)
let d := double_with_se(i)
}
// ====
// step: commonSubexpressionEliminator
// ----
// {
// function double(x) -> y
// { y := add(x, x) }
// function double_with_se(x_1) -> y_2
// {
// y_2 := add(x_1, x_1)
// mstore(40, 4)
// }
// let i := mload(3)
// let a := double(i)
// let b := a
// let c := double_with_se(i)
// let d := double_with_se(i)
// }
@@ -1,6 +1,6 @@
// Even if the functions pass the equality check, they are not movable.
{
function f() -> a { }
function f() -> a { mstore(1, 2) }
let b := sub(f(), f())
mstore(0, b)
}
@@ -9,6 +9,6 @@
// ----
// {
// function f() -> a
// { }
// { mstore(1, 2) }
// mstore(0, sub(f(), f()))
// }
@@ -31,8 +31,5 @@
// mstore8(calldataload(_5), 4)
// sstore(_5, mload(_2))
// mstore(_2, _17)
// g()
// sstore(_5, mload(_2))
// function g()
// { }
// }
@@ -0,0 +1,13 @@
{
function f(x) -> t {
let b := 7
}
function g(x) -> t {
t := x
}
let x := f(g(2))
}
// ====
// step: unusedPruner
// ----
// { }
@@ -5,8 +5,4 @@
// ====
// step: unusedPruner
// ----
// {
// function f() -> x, y
// { }
// let a, b := f()
// }
// { }
+561 -233
View File
@@ -22,8 +22,10 @@
#include <libdevcore/StringUtils.h>
#include <boost/range/algorithm_ext/erase.hpp>
#include <boost/algorithm/cxx11/all_of.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/range/algorithm_ext/erase.hpp>
using namespace std;
using namespace yul::test::yul_fuzzer;
@@ -84,11 +86,34 @@ string ProtoConverter::visit(Literal const& _x)
}
}
// Reference any index in [0, m_numLiveVars-1]
bool ProtoConverter::varDeclAvailable()
{
if (m_inFunctionDef)
return m_scopeVars.top().size() > 0;
else
return m_variables.size() > 0;
}
bool ProtoConverter::functionCallNotPossible(FunctionCall_Returns _type)
{
return _type == FunctionCall::SINGLE ||
(_type == FunctionCall::MULTIASSIGN && !varDeclAvailable());
}
void ProtoConverter::visit(VarRef const& _x)
{
yulAssert(m_numLiveVars > 0, "Proto fuzzer: No variables to reference.");
m_output << "x_" << (static_cast<uint32_t>(_x.varnum()) % m_numLiveVars);
if (m_inFunctionDef)
{
// Ensure that there is at least one variable declaration to reference in function scope.
yulAssert(m_scopeVars.top().size() > 0, "Proto fuzzer: No variables to reference.");
m_output << m_scopeVars.top()[_x.varnum() % m_scopeVars.top().size()];
}
else
{
// Ensure that there is at least one variable declaration to reference in nested scopes.
yulAssert(m_variables.size() > 0, "Proto fuzzer: No variables to reference.");
m_output << m_variables[_x.varnum() % m_variables.size()];
}
}
void ProtoConverter::visit(Expression const& _x)
@@ -96,7 +121,13 @@ void ProtoConverter::visit(Expression const& _x)
switch (_x.expr_oneof_case())
{
case Expression::kVarref:
visit(_x.varref());
// If the expression requires a variable reference that we cannot provide
// (because there are no variables in scope), we silently output a literal
// expression from the optimizer dictionary.
if (!varDeclAvailable())
m_output << dictionaryToken();
else
visit(_x.varref());
break;
case Expression::kCons:
m_output << visit(_x.cons());
@@ -114,7 +145,18 @@ void ProtoConverter::visit(Expression const& _x)
visit(_x.nop());
break;
case Expression::kFuncExpr:
visit(_x.func_expr());
// FunctionCall must return a single value, otherwise
// we output a trivial expression "1".
if (_x.func_expr().ret() == FunctionCall::SINGLE)
visit(_x.func_expr());
else
m_output << dictionaryToken();
break;
case Expression::kLowcall:
visit(_x.lowcall());
break;
case Expression::kCreate:
visit(_x.create());
break;
case Expression::EXPR_ONEOF_NOT_SET:
m_output << dictionaryToken();
@@ -202,54 +244,18 @@ void ProtoConverter::visit(BinaryOp const& _x)
void ProtoConverter::visit(VarDecl const& _x)
{
m_output << "let x_" << m_numLiveVars << " := ";
string varName = newVarName();
m_output << "let " << varName << " := ";
visit(_x.expr());
m_numVarsPerScope.top()++;
m_numLiveVars++;
m_output << "\n";
}
void ProtoConverter::visit(EmptyVarDecl const&)
{
m_output << "let x_" << m_numLiveVars++ << "\n";
m_numVarsPerScope.top()++;
}
void ProtoConverter::visit(MultiVarDecl const& _x)
{
size_t funcId = (static_cast<size_t>(_x.func_index()) % m_functionVecMultiReturnValue.size());
int numInParams = m_functionVecMultiReturnValue.at(funcId).first;
int numOutParams = m_functionVecMultiReturnValue.at(funcId).second;
// Ensure that the chosen function returns at least 2 and at most 4 values
yulAssert(
((numOutParams >= 2) && (numOutParams <= 4)),
"Proto fuzzer: Multi variable declaration calls a function with either too few or too many output params."
);
// We must start variable numbering past the number of live variables at this point in time.
// This creates let x_p,..., x_k :=
// (k-p)+1 = numOutParams
m_output <<
"let " <<
dev::suffixedVariableNameList("x_", m_numLiveVars, m_numLiveVars + numOutParams) <<
" := ";
// Create RHS of multi var decl
m_output << "foo_" << functionTypeToString(NumFunctionReturns::Multiple) << "_" << funcId;
m_output << "(";
visitFunctionInputParams(_x, numInParams);
m_output << ")\n";
// Update live variables in scope and in total to account for the variables created by this
// multi variable declaration.
m_numVarsPerScope.top() += numOutParams;
m_numLiveVars += numOutParams;
m_scopeVars.top().push_back(varName);
m_variables.push_back(varName);
}
void ProtoConverter::visit(TypedVarDecl const& _x)
{
m_output << "let x_" << m_numLiveVars;
string varName = newVarName();
m_output << "let " << varName;
switch (_x.type())
{
case TypedVarDecl::BOOL:
@@ -308,8 +314,8 @@ void ProtoConverter::visit(TypedVarDecl const& _x)
m_output << " : u256\n";
break;
}
m_numVarsPerScope.top()++;
m_numLiveVars++;
m_scopeVars.top().push_back(varName);
m_variables.push_back(varName);
}
void ProtoConverter::visit(UnaryOp const& _x)
@@ -337,6 +343,12 @@ void ProtoConverter::visit(UnaryOp const& _x)
case UnaryOp::EXTCODEHASH:
m_output << "extcodehash";
break;
case UnaryOp::BALANCE:
m_output << "balance";
break;
case UnaryOp::BLOCKHASH:
m_output << "blockhash";
break;
}
m_output << "(";
visit(_x.operand());
@@ -385,6 +397,36 @@ void ProtoConverter::visit(NullaryOp const& _x)
case NullaryOp::RETURNDATASIZE:
m_output << "returndatasize()";
break;
case NullaryOp::ADDRESS:
m_output << "address()";
break;
case NullaryOp::ORIGIN:
m_output << "origin()";
break;
case NullaryOp::CALLER:
m_output << "caller()";
break;
case NullaryOp::CALLVALUE:
m_output << "callvalue()";
break;
case NullaryOp::GASPRICE:
m_output << "gasprice()";
break;
case NullaryOp::COINBASE:
m_output << "coinbase()";
break;
case NullaryOp::TIMESTAMP:
m_output << "timestamp()";
break;
case NullaryOp::NUMBER:
m_output << "number()";
break;
case NullaryOp::DIFFICULTY:
m_output << "difficulty()";
break;
case NullaryOp::GASLIMIT:
m_output << "gaslimit()";
break;
}
}
@@ -500,9 +542,7 @@ void ProtoConverter::visit(AssignmentStatement const& _x)
m_output << "\n";
}
// Called at the time function call is being made
template <class T>
void ProtoConverter::visitFunctionInputParams(T const& _x, unsigned _numInputParams)
void ProtoConverter::visitFunctionInputParams(FunctionCall const& _x, unsigned _numInputParams)
{
// We reverse the order of function input visits since it helps keep this switch case concise.
switch (_numInputParams)
@@ -530,87 +570,217 @@ void ProtoConverter::visitFunctionInputParams(T const& _x, unsigned _numInputPar
}
}
void ProtoConverter::visit(MultiAssignment const& _x)
bool ProtoConverter::functionValid(FunctionCall_Returns _type, unsigned _numOutParams)
{
size_t funcId = (static_cast<size_t>(_x.func_index()) % m_functionVecMultiReturnValue.size());
unsigned numInParams = m_functionVecMultiReturnValue.at(funcId).first;
unsigned numOutParams = m_functionVecMultiReturnValue.at(funcId).second;
yulAssert(
((numOutParams >= 2) && (numOutParams <= 4)),
"Proto fuzzer: Multi assignment calls a function that has either too many or too few output parameters."
);
// Convert LHS of multi assignment
// We reverse the order of out param visits since the order does not matter. This helps reduce the size of this
// switch statement.
switch (numOutParams)
switch (_type)
{
case 4:
visit(_x.out_param4());
m_output << ", ";
BOOST_FALLTHROUGH;
case 3:
visit(_x.out_param3());
m_output << ", ";
BOOST_FALLTHROUGH;
case 2:
visit(_x.out_param2());
m_output << ", ";
visit(_x.out_param1());
break;
default:
yulAssert(false, "Proto fuzzer: Function call with too many input parameters.");
break;
case FunctionCall::ZERO:
return _numOutParams == 0;
case FunctionCall::SINGLE:
return _numOutParams == 1;
case FunctionCall::MULTIDECL:
case FunctionCall::MULTIASSIGN:
return _numOutParams > 1;
}
m_output << " := ";
// Convert RHS of multi assignment
m_output << "foo_" << functionTypeToString(NumFunctionReturns::Multiple) << "_" << funcId;
m_output << "(";
visitFunctionInputParams(_x, numInParams);
m_output << ")\n";
}
void ProtoConverter::visit(FunctionCallNoReturnVal const& _x)
void ProtoConverter::convertFunctionCall(
FunctionCall const& _x,
std::string _name,
unsigned _numInParams,
bool _newLine
)
{
size_t funcId = (static_cast<size_t>(_x.func_index()) % m_functionVecNoReturnValue.size());
unsigned numInParams = m_functionVecNoReturnValue.at(funcId);
m_output << "foo_" << functionTypeToString(NumFunctionReturns::None) << "_" << funcId;
m_output << "(";
visitFunctionInputParams(_x, numInParams);
m_output << ")\n";
}
void ProtoConverter::visit(FunctionCallSingleReturnVal const& _x)
{
size_t funcId = (static_cast<size_t>(_x.func_index()) % m_functionVecSingleReturnValue.size());
unsigned numInParams = m_functionVecSingleReturnValue.at(funcId);
m_output << "foo_" << functionTypeToString(NumFunctionReturns::Single) << "_" << funcId;
m_output << "(";
visitFunctionInputParams(_x, numInParams);
m_output << _name << "(";
visitFunctionInputParams(_x, _numInParams);
m_output << ")";
if (_newLine)
m_output << "\n";
}
vector<string> ProtoConverter::createVarDecls(unsigned _start, unsigned _end, bool _isAssignment)
{
m_output << "let ";
vector<string> varsVec = createVars(_start, _end);
if (_isAssignment)
m_output << " := ";
else
m_output << "\n";
return varsVec;
}
void ProtoConverter::visit(FunctionCall const& _x)
{
switch (_x.functioncall_oneof_case())
bool functionAvailable = m_functionSigMap.size() > 0;
unsigned numInParams, numOutParams;
string funcName;
FunctionCall_Returns funcType = _x.ret();
if (functionAvailable)
{
case FunctionCall::kCallZero:
visit(_x.call_zero());
yulAssert(m_functions.size() > 0, "Proto fuzzer: No function in scope");
funcName = m_functions[_x.func_index() % m_functions.size()];
auto ret = m_functionSigMap.at(funcName);
numInParams = ret.first;
numOutParams = ret.second;
}
else
{
// If there are no functions available, calls to functions that
// return a single value may be replaced by a dictionary token.
if (funcType == FunctionCall::SINGLE)
m_output << dictionaryToken();
return;
}
// If function selected for function call does not meet interface
// requirements (num output values) for the function type
// specified, then we return early unless it is a function call
// that returns a single value (which may be replaced by a
// dictionary token.
if (!functionValid(funcType, numOutParams))
{
if (funcType == FunctionCall::SINGLE)
m_output << dictionaryToken();
return;
}
// If we are here, it means that we have at least one valid
// function for making the function call
switch (funcType)
{
case FunctionCall::ZERO:
convertFunctionCall(_x, funcName, numInParams);
break;
case FunctionCall::kCallMultidecl:
// Hack: Disallow (multi) variable declarations until scope extension is implemented for "for-init"
case FunctionCall::SINGLE:
// Since functions that return a single value are used as expressions
// we do not print a newline because it is done by the expression
// visitor.
convertFunctionCall(_x, funcName, numInParams, /*newLine=*/false);
break;
case FunctionCall::MULTIDECL:
// Hack: Disallow (multi) variable declarations until scope extension
// is implemented for "for-init"
if (!m_inForInitScope)
visit(_x.call_multidecl());
{
// Ensure that the chosen function returns at most 4 values
yulAssert(
numOutParams <= 4,
"Proto fuzzer: Function call with too many output params encountered."
);
// Obtain variable name suffix
unsigned startIdx = counter();
vector<string> varsVec = createVarDecls(
startIdx,
startIdx + numOutParams,
/*isAssignment=*/true
);
// Create RHS of multi var decl
convertFunctionCall(_x, funcName, numInParams);
// Add newly minted vars in the multidecl statement to current scope
addVarsToScope(varsVec);
}
break;
case FunctionCall::kCallMultiassign:
visit(_x.call_multiassign());
break;
case FunctionCall::FUNCTIONCALL_ONEOF_NOT_SET:
case FunctionCall::MULTIASSIGN:
// Ensure that the chosen function returns at most 4 values
yulAssert(
numOutParams <= 4,
"Proto fuzzer: Function call with too many output params encountered."
);
// Convert LHS of multi assignment
// We reverse the order of out param visits since the order does not matter.
// This helps reduce the size of this switch statement.
switch (numOutParams)
{
case 4:
visit(_x.out_param4());
m_output << ", ";
BOOST_FALLTHROUGH;
case 3:
visit(_x.out_param3());
m_output << ", ";
BOOST_FALLTHROUGH;
case 2:
visit(_x.out_param2());
m_output << ", ";
visit(_x.out_param1());
break;
default:
yulAssert(false, "Proto fuzzer: Function call with too many or too few input parameters.");
break;
}
m_output << " := ";
// Convert RHS of multi assignment
convertFunctionCall(_x, funcName, numInParams);
break;
}
}
void ProtoConverter::visit(LowLevelCall const& _x)
{
LowLevelCall_Type type = _x.callty();
switch (type)
{
case LowLevelCall::CALL:
m_output << "call(";
break;
case LowLevelCall::CALLCODE:
m_output << "callcode(";
break;
case LowLevelCall::DELEGATECALL:
m_output << "delegatecall(";
break;
case LowLevelCall::STATICCALL:
m_output << "staticcall(";
break;
}
visit(_x.gas());
m_output << ", ";
visit(_x.addr());
m_output << ", ";
if (type == LowLevelCall::CALL || LowLevelCall::CALLCODE)
{
visit(_x.wei());
m_output << ", ";
}
visit(_x.in());
m_output << ", ";
visit(_x.insize());
m_output << ", ";
visit(_x.out());
m_output << ", ";
visit(_x.outsize());
m_output << ")";
}
void ProtoConverter::visit(Create const& _x)
{
Create_Type type = _x.createty();
switch (type)
{
case Create::CREATE:
m_output << "create(";
break;
case Create::CREATE2:
m_output << "create2(";
break;
}
visit(_x.wei());
m_output << ", ";
visit(_x.position());
m_output << ", ";
visit(_x.size());
if (type == Create::CREATE2)
{
m_output << ", ";
visit(_x.value());
}
m_output << ")";
}
void ProtoConverter::visit(IfStmt const& _x)
{
m_output << "if ";
@@ -693,7 +863,7 @@ void ProtoConverter::visit(CaseStmt const& _x)
// a case statement containing a case literal that has already been used in a
// previous case statement. If the hash (u256 value) matches a previous hash,
// then we simply don't create a new case statement.
string noDoubleQuoteStr = "";
string noDoubleQuoteStr{""};
if (literal.size() > 2)
{
// Ensure that all characters in the string literal except the first
@@ -742,7 +912,7 @@ void ProtoConverter::visit(SwitchStmt const& _x)
{
if (_x.case_stmt_size() > 0 || _x.has_default_block())
{
std::set<dev::u256> s;
std::set<u256> s;
m_switchLiteralSetPerScope.push(s);
m_output << "switch ";
visit(_x.switch_expr());
@@ -828,7 +998,10 @@ void ProtoConverter::visit(Statement const& _x)
visit(_x.decl());
break;
case Statement::kAssignment:
visit(_x.assignment());
// Create an assignment statement only if there is at least one variable
// declaration that is in scope.
if (varDeclAvailable())
visit(_x.assignment());
break;
case Statement::kIfstmt:
visit(_x.ifstmt());
@@ -869,133 +1042,309 @@ void ProtoConverter::visit(Statement const& _x)
visit(_x.terminatestmt());
break;
case Statement::kFunctioncall:
// Return early if a function call cannot be created
if (functionCallNotPossible(_x.functioncall().ret()))
return;
visit(_x.functioncall());
break;
case Statement::kFuncdef:
if (!m_inForInitScope)
visit(_x.funcdef());
break;
case Statement::kPop:
visit(_x.pop());
break;
case Statement::STMT_ONEOF_NOT_SET:
break;
}
}
void ProtoConverter::visit(Block const& _x)
void ProtoConverter::openScope(vector<string> const& _funcParams)
{
m_scopeVars.push({});
m_scopeFuncs.push({});
if (!_funcParams.empty())
addVarsToScope(_funcParams);
}
void ProtoConverter::updateFunctionMaps(string const& _var)
{
unsigned erased = m_functionSigMap.erase(_var);
for (auto const& i: m_functionDefMap)
if (i.second == _var)
{
erased += m_functionDefMap.erase(i.first);
break;
}
yulAssert(erased == 2, "Proto fuzzer: Function maps not updated");
}
void ProtoConverter::closeScope()
{
for (auto const& var: m_scopeVars.top())
{
unsigned numVarsRemoved = m_variables.size();
m_variables.erase(remove(m_variables.begin(), m_variables.end(), var), m_variables.end());
numVarsRemoved -= m_variables.size();
yulAssert(
numVarsRemoved == 1,
"Proto fuzzer: Nothing or too much went out of scope"
);
}
m_scopeVars.pop();
for (auto const& f: m_scopeFuncs.top())
{
unsigned numFuncsRemoved = m_functions.size();
m_functions.erase(remove(m_functions.begin(), m_functions.end(), f), m_functions.end());
numFuncsRemoved -= m_functions.size();
yulAssert(
numFuncsRemoved == 1,
"Proto fuzzer: Nothing or too much went out of scope"
);
updateFunctionMaps(f);
}
m_scopeFuncs.pop();
}
void ProtoConverter::addVarsToScope(vector<string> const& _vars)
{
for (string const& i: _vars)
{
m_variables.push_back(i);
m_scopeVars.top().push_back(i);
}
}
void ProtoConverter::visit(Block const& _x, vector<string> _funcParams)
{
openScope(_funcParams);
// Register function declarations in this scope unless this
// scope belongs to for-init (in which function declarations
// are forbidden).
for (auto const& statement: _x.statements())
if (statement.has_funcdef() && !m_inForInitScope)
registerFunction(&statement.funcdef());
if (_x.statements_size() > 0)
{
m_numVarsPerScope.push(0);
m_output << "{\n";
for (auto const& st: _x.statements())
visit(st);
m_output << "}\n";
m_numLiveVars -= m_numVarsPerScope.top();
m_numVarsPerScope.pop();
}
else
m_output << "{}\n";
closeScope();
}
void ProtoConverter::visit(SpecialBlock const& _x)
vector<string> ProtoConverter::createVars(unsigned _startIdx, unsigned _endIdx)
{
m_numVarsPerScope.push(0);
m_output << "{\n";
visit(_x.var());
if (_x.statements_size() > 0)
for (auto const& st: _x.statements())
visit(st);
m_numLiveVars -= m_numVarsPerScope.top();
m_numVarsPerScope.pop();
m_output << "}\n";
}
template <class T>
void ProtoConverter::createFunctionDefAndCall(T const& _x, unsigned _numInParams, unsigned _numOutParams, NumFunctionReturns _type)
{
yulAssert(
((_numInParams <= modInputParams - 1) && (_numOutParams <= modOutputParams - 1)),
"Proto fuzzer: Too many function I/O parameters requested."
yulAssert(_endIdx > _startIdx, "Proto fuzzer: Variable indices not in range");
string varsStr = dev::suffixedVariableNameList("x_", _startIdx, _endIdx);
m_output << varsStr;
vector<string> varsVec;
boost::split(
varsVec,
varsStr,
boost::algorithm::is_any_of(", "),
boost::algorithm::token_compress_on
);
// At the time of function definition creation, the number of live variables must be 0.
// This is because we always create only as many variables as we need within function scope.
yulAssert(m_numLiveVars == 0, "Proto fuzzer: Unused live variable found.");
yulAssert(
varsVec.size() == (_endIdx - _startIdx),
"Proto fuzzer: Variable count mismatch during function definition"
);
m_counter += varsVec.size();
return varsVec;
}
// Signature
// This creates function foo_<noreturn|singlereturn|multireturn>_<m_numFunctionSets>(x_0,...,x_n)
m_output << "function foo_" << functionTypeToString(_type) << "_" << m_numFunctionSets;
m_output << "(";
if (_numInParams > 0)
m_output << dev::suffixedVariableNameList("x_", 0, _numInParams);
m_output << ")";
void ProtoConverter::registerFunction(FunctionDef const* _x)
{
unsigned numInParams = _x->num_input_params() % s_modInputParams;
unsigned numOutParams = _x->num_output_params() % s_modOutputParams;
NumFunctionReturns numReturns;
if (numOutParams == 0)
numReturns = NumFunctionReturns::None;
else if (numOutParams == 1)
numReturns = NumFunctionReturns::Single;
else
numReturns = NumFunctionReturns::Multiple;
// Book keeping for variables in function scope and in nested scopes
m_numVarsPerScope.push(_numInParams);
m_numLiveVars += _numInParams;
// Generate function name
string funcName = functionName(numReturns);
// This creates -> x_n+1,...,x_r
if (_numOutParams > 0)
{
m_output << " -> " << dev::suffixedVariableNameList("x_", _numInParams, _numInParams + _numOutParams);
// More bookkeeping
m_numVarsPerScope.top() += _numOutParams;
m_numLiveVars += _numOutParams;
}
m_output << "\n";
// Register function
auto ret = m_functionSigMap.emplace(make_pair(funcName, make_pair(numInParams, numOutParams)));
yulAssert(ret.second, "Proto fuzzer: Function already exists.");
m_functions.push_back(funcName);
m_scopeFuncs.top().push_back(funcName);
m_functionDefMap.emplace(make_pair(_x, funcName));
}
// Body
visit(_x.statements());
// Ensure that variable stack is balanced
m_numLiveVars -= m_numVarsPerScope.top();
m_numVarsPerScope.pop();
yulAssert(m_numLiveVars == 0, "Proto fuzzer: Variable stack after function definition is unbalanced.");
// Manually create a multi assignment using global variables
// This prints a_0, ..., a_k-1 for this function that returns "k" values
if (_numOutParams > 0)
m_output << dev::suffixedVariableNameList("a_", 0, _numOutParams) << " := ";
// Call the function with the correct number of input parameters via calls to calldataload with
// incremental addresses.
m_output << "foo_" << functionTypeToString(_type) << "_" << std::to_string(m_numFunctionSets);
m_output << "(";
void ProtoConverter::fillFunctionCallInput(unsigned _numInParams)
{
for (unsigned i = 0; i < _numInParams; i++)
{
m_output << "calldataload(" << std::to_string(i*32) << ")";
// Throw a 4-sided dice to choose whether to populate function input
// argument from a pseudo-randomly chosen slot in one of the following
// locations: calldata, memory, storage, or yul optimizer dictionary.
unsigned diceValue = counter() % 4;
// Pseudo-randomly choose one of the first ten 32-byte
// aligned slots.
string slot = to_string((counter() % 10) * 32);
switch (diceValue)
{
case 0:
m_output << "calldataload(" << slot << ")";
break;
case 1:
m_output << "mload(" << slot << ")";
break;
case 2:
m_output << "sload(" << slot << ")";
break;
case 3:
// Call to dictionaryToken() automatically picks a token
// at a pseudo-random location.
m_output << dictionaryToken();
break;
}
if (i < _numInParams - 1)
m_output << ",";
}
}
void ProtoConverter::saveFunctionCallOutput(vector<string> const& _varsVec)
{
for (auto const& var: _varsVec)
{
// Flip a dice to choose whether to save output values
// in storage or memory.
bool coinFlip = counter() % 2 == 0;
// Pseudo-randomly choose one of the first ten 32-byte
// aligned slots.
string slot = to_string((counter() % 10) * 32);
if (coinFlip)
m_output << "sstore(" << slot << ", " << var << ")\n";
else
m_output << "mstore(" << slot << ", " << var << ")\n";
}
}
void ProtoConverter::createFunctionCall(
string _funcName,
unsigned _numInParams,
unsigned _numOutParams
)
{
vector<string> varsVec{};
if (_numOutParams > 0)
{
unsigned startIdx = counter();
// Prints the following to output stream "let x_i,...,x_n := "
varsVec = createVarDecls(
startIdx,
startIdx + _numOutParams,
/*isAssignment=*/true
);
}
// Call the function with the correct number of input parameters
m_output << _funcName << "(";
if (_numInParams > 0)
fillFunctionCallInput(_numInParams);
m_output << ")\n";
for (unsigned i = 0; i < _numOutParams; i++)
m_output << "sstore(" << std::to_string(i*32) << ", a_" << std::to_string(i) << ")\n";
if (!varsVec.empty())
{
// Save values returned by function so that they are reflected
// in the interpreter trace.
saveFunctionCallOutput(varsVec);
// Add newly minted vars to current scope
addVarsToScope(varsVec);
}
else
yulAssert(_numOutParams == 0, "Proto fuzzer: Function return value not saved");
}
void ProtoConverter::visit(FunctionDefinitionNoReturnVal const& _x)
void ProtoConverter::createFunctionDefAndCall(
FunctionDef const& _x,
unsigned _numInParams,
unsigned _numOutParams
)
{
unsigned numInParams = _x.num_input_params() % modInputParams;
unsigned numOutParams = 0;
createFunctionDefAndCall(_x, numInParams, numOutParams, NumFunctionReturns::None);
yulAssert(
((_numInParams <= s_modInputParams - 1) && (_numOutParams <= s_modOutputParams - 1)),
"Proto fuzzer: Too many function I/O parameters requested."
);
// Obtain function name
yulAssert(m_functionDefMap.count(&_x), "Proto fuzzer: Unregistered function");
string funcName = m_functionDefMap.at(&_x);
vector<string> varsVec = {};
m_output << "function " << funcName << "(";
unsigned startIdx = counter();
if (_numInParams > 0)
varsVec = createVars(startIdx, startIdx + _numInParams);
m_output << ")";
vector<string> outVarsVec = {};
// This creates -> x_n+1,...,x_r
if (_numOutParams > 0)
{
m_output << " -> ";
if (varsVec.empty())
{
yulAssert(_numInParams == 0, "Proto fuzzer: Input parameters not processed correctly");
varsVec = createVars(startIdx, startIdx + _numOutParams);
}
else
{
outVarsVec = createVars(startIdx + _numInParams, startIdx + _numInParams + _numOutParams);
varsVec.insert(varsVec.end(), outVarsVec.begin(), outVarsVec.end());
}
}
yulAssert(varsVec.size() == _numInParams + _numOutParams, "Proto fuzzer: Function parameters not processed correctly");
m_output << "\n";
// If function definition is in for-loop body, update
bool wasInForBody = m_inForBodyScope;
m_inForBodyScope = false;
bool wasInFunctionDef = m_inFunctionDef;
m_inFunctionDef = true;
// Body
visit(_x.block(), varsVec);
m_inForBodyScope = wasInForBody;
m_inFunctionDef = wasInFunctionDef;
yulAssert(
!m_inForInitScope,
"Proto fuzzer: Trying to create function call inside for-init block"
);
createFunctionCall(funcName, _numInParams, _numOutParams);
}
void ProtoConverter::visit(FunctionDefinitionSingleReturnVal const& _x)
void ProtoConverter::visit(FunctionDef const& _x)
{
unsigned numInParams = _x.num_input_params() % modInputParams;
unsigned numOutParams = 1;
createFunctionDefAndCall(_x, numInParams, numOutParams, NumFunctionReturns::Single);
unsigned numInParams = _x.num_input_params() % s_modInputParams;
unsigned numOutParams = _x.num_output_params() % s_modOutputParams;
createFunctionDefAndCall(_x, numInParams, numOutParams);
}
void ProtoConverter::visit(FunctionDefinitionMultiReturnVal const& _x)
void ProtoConverter::visit(PopStmt const& _x)
{
unsigned numInParams = _x.num_input_params() % modInputParams;
// Synthesize at least 2 return parameters and at most (modOutputParams - 1)
unsigned numOutParams = std::max<unsigned>(2, _x.num_output_params() % modOutputParams);
createFunctionDefAndCall(_x, numInParams, numOutParams, NumFunctionReturns::Multiple);
}
void ProtoConverter::visit(FunctionDefinition const& _x)
{
visit(_x.fd_zero());
visit(_x.fd_one());
visit(_x.fd_multi());
m_numFunctionSets++;
m_output << "pop(";
visit(_x.expr());
m_output << ")\n";
}
void ProtoConverter::visit(Program const& _x)
@@ -1004,25 +1353,14 @@ void ProtoConverter::visit(Program const& _x)
m_inputSize = _x.ByteSizeLong();
/* Program template is as follows
* Four Globals a_0, a_1, a_2, and a_3 to hold up to four function return values
*
* Repeated function definitions followed by function calls of the respective function
* Zero or more statements. If function definition is present, it is
* called post definition.
* Example: function foo(x_0) -> x_1 {}
* a_0 := foo(calldataload(0))
* sstore(0, a_0)
* x_2 := foo(calldataload(0))
* sstore(0, x_2)
*/
m_output << "{\n";
// Create globals at the beginning
// This creates let a_0, a_1, a_2, a_3 (followed by a new line)
m_output << "let " << dev::suffixedVariableNameList("a_", 0, modOutputParams - 1) << "\n";
// Register function interface. Useful while visiting multi var decl/assignment statements.
for (auto const& f: _x.funcs())
registerFunction(f);
for (auto const& f: _x.funcs())
visit(f);
yulAssert((unsigned)_x.funcs_size() == m_numFunctionSets, "Proto fuzzer: Functions not correctly registered.");
visit(_x.block());
m_output << "}\n";
}
@@ -1032,25 +1370,15 @@ string ProtoConverter::programToString(Program const& _input)
return m_output.str();
}
void ProtoConverter::registerFunction(FunctionDefinition const& _x)
{
// No return and single return functions explicitly state the number of values returned
registerFunction(_x.fd_zero(), NumFunctionReturns::None);
registerFunction(_x.fd_one(), NumFunctionReturns::Single);
// A multi return function can have between two and (modOutputParams - 1) parameters
unsigned numOutParams = std::max<unsigned>(2, _x.fd_multi().num_output_params() % modOutputParams);
registerFunction(_x.fd_multi(), NumFunctionReturns::Multiple, numOutParams);
}
std::string ProtoConverter::functionTypeToString(NumFunctionReturns _type)
{
switch (_type)
{
case NumFunctionReturns::None:
return "noreturn";
return "n";
case NumFunctionReturns::Single:
return "singlereturn";
return "s";
case NumFunctionReturns::Multiple:
return "multireturn";
return "m";
}
}
}
+167 -50
View File
@@ -40,14 +40,12 @@ class ProtoConverter
public:
ProtoConverter()
{
m_numLiveVars = 0;
m_numVarsPerScope.push(m_numLiveVars);
m_numFunctionSets = 0;
m_inForBodyScope = false;
m_inForInitScope = false;
m_numNestedForLoops = 0;
m_counter = 0;
m_inputSize = 0;
m_inFunctionDef = false;
}
ProtoConverter(ProtoConverter const&) = delete;
ProtoConverter(ProtoConverter&&) = delete;
@@ -55,22 +53,23 @@ public:
private:
void visit(BinaryOp const&);
void visit(Block const&);
void visit(SpecialBlock const&);
/// Visits a basic block optionally adding @a _funcParams to scope.
/// @param _block Reference to a basic block of yul statements.
/// @param _funcParams List of function parameter names, defaults to
/// an empty vector.
void visit(Block const& _block, std::vector<std::string> _funcParams = {});
std::string visit(Literal const&);
void visit(VarRef const&);
void visit(Expression const&);
void visit(VarDecl const&);
void visit(EmptyVarDecl const&);
void visit(MultiVarDecl const&);
void visit(TypedVarDecl const&);
void visit(UnaryOp const&);
void visit(AssignmentStatement const&);
void visit(MultiAssignment const&);
void visit(IfStmt const&);
void visit(StoreFunc const&);
void visit(Statement const&);
void visit(FunctionDefinition const&);
void visit(ForStmt const&);
void visit(BoundedForStmt const&);
void visit(CaseStmt const&);
@@ -84,17 +83,29 @@ private:
void visit(RetRevStmt const&);
void visit(SelfDestructStmt const&);
void visit(TerminatingStmt const&);
void visit(FunctionCallNoReturnVal const&);
void visit(FunctionCallSingleReturnVal const&);
void visit(FunctionCall const&);
void visit(FunctionDefinitionNoReturnVal const&);
void visit(FunctionDefinitionSingleReturnVal const&);
void visit(FunctionDefinitionMultiReturnVal const&);
void visit(FunctionDef const&);
void visit(PopStmt const&);
void visit(LowLevelCall const&);
void visit(Create const&);
void visit(Program const&);
void registerFunction(FunctionDefinition const&);
/// Creates a new scope, and adds @a _funcParams to it if it
/// is non-empty.
void openScope(std::vector<std::string> const& _funcParams);
/// Closes current scope
void closeScope();
/// Adds @a _vars to current scope
void addVarsToScope(std::vector<std::string> const& _vars);
std::string createHex(std::string const& _hexBytes);
/// Returns a new variable name.
std::string newVarName()
{
return "x_" + std::to_string(counter());
}
/// Accepts an arbitrary string, removes all characters that are neither
/// alphabets nor digits from it and returns the said string.
std::string createAlphaNum(std::string const& _strBytes);
@@ -105,30 +116,123 @@ private:
Multiple
};
template<class T>
void visitFunctionInputParams(T const&, unsigned);
void visitFunctionInputParams(FunctionCall const&, unsigned);
void createFunctionDefAndCall(FunctionDef const&, unsigned, unsigned);
template<class T>
void createFunctionDefAndCall(T const&, unsigned, unsigned, NumFunctionReturns);
/// Convert function type to a string to be used while naming a
/// function that is created by a function declaration statement.
/// @param _type Type classified according to the number of
/// values returned by function.
/// @return A string as follows. If _type is
/// None -> "n"
/// Single -> "s"
/// Multiple -> "m"
std::string functionTypeToString(NumFunctionReturns _type);
template <class T>
void registerFunction(T const& _x, NumFunctionReturns _type, unsigned _numOutputParams = 0)
{
unsigned numInputParams = _x.num_input_params() % modInputParams;
switch (_type)
{
case NumFunctionReturns::None:
m_functionVecNoReturnValue.push_back(numInputParams);
break;
case NumFunctionReturns::Single:
m_functionVecSingleReturnValue.push_back(numInputParams);
break;
case NumFunctionReturns::Multiple:
m_functionVecMultiReturnValue.push_back(std::make_pair(numInputParams, _numOutputParams));
break;
}
}
/// Return true if at least one variable declaration is in scope,
/// false otherwise.
/// @return True in the following cases:
/// - If we are inside a function that has already declared a variable
/// - If there is at least one variable declaration that is
/// in scope
bool varDeclAvailable();
/// Return true if a function call cannot be made, false otherwise.
/// @param _type is an enum denoting the type of function call. It
/// can be one of NONE, SINGLE, MULTIDECL, MULTIASSIGN.
/// NONE -> Function call does not return a value
/// SINGLE -> Function call returns a single value
/// MULTIDECL -> Function call returns more than one value
/// and it is used to create a multi declaration
/// statement
/// MULTIASSIGN -> Function call returns more than one value
/// and it is used to create a multi assignment
/// statement
/// @return True if the function call cannot be created for one of the
/// following reasons
// - It is a SINGLE function call (we reserve SINGLE functions for
// expressions)
// - It is a MULTIASSIGN function call and we do not have any
// variables available for assignment.
bool functionCallNotPossible(FunctionCall_Returns _type);
/// Checks if function call of type @a _type returns the correct number
/// of values.
/// @param _type Function call type of the function being checked
/// @param _numOutParams Number of values returned by the function
/// being checked
/// @return true if the function returns the correct number of values,
/// false otherwise
bool functionValid(FunctionCall_Returns _type, unsigned _numOutParams);
/// Converts protobuf function call to a yul function call and appends
/// it to output stream.
/// @param _x Protobuf function call
/// @param _name Function name
/// @param _numInParams Number of input arguments accepted by function
/// @param _newLine Flag that prints a new line to the output stream if
/// true. Default value for the flag is true.
void convertFunctionCall(
FunctionCall const& _x,
std::string _name,
unsigned _numInParams,
bool _newLine = true
);
/// Prints a yul formatted variable declaration statement to the output
/// stream.
/// Example 1: createVarDecls(0, 1, true) returns {"x_0"} and prints
/// let x_0 :=
/// Example 2: createVarDecls(0, 2, false) returns {"x_0", "x_1"} and prints
/// let x_0, x_1
/// @param _start Start index of variable (inclusive)
/// @param _end End index of variable (exclusive)
/// @param _isAssignment Flag indicating if variable declaration is also
/// an assignment. If true, the string " := " follows the variable
/// declaration. Otherwise, a new line is follows the variable
/// declaration.
/// @return A vector of strings containing the variable names used in
/// the declaration statement.
std::vector<std::string> createVarDecls(unsigned _start, unsigned _end, bool _isAssignment);
/// Prints comma separated variable names to output stream and
/// returns a vector containing the printed variable names.
/// Example: createVars(0, 2) returns {"x_0", "x_1"} and prints
/// x_0, x_1
/// @param _startIdx Start index of variable (inclusive)
/// @param _endIdx End index of variable (exclusive)
/// @return A vector of strings containing the printed variable names.
std::vector<std::string> createVars(unsigned _startIdx, unsigned _endIdx);
/// Print the yul syntax to make a call to a function named @a _funcName to
/// the output stream.
/// @param _funcName Name of the function to be called
/// @param _numInParams Number of input parameters in function signature
/// @param _numOutParams Number of output parameters in function signature
void createFunctionCall(std::string _funcName, unsigned _numInParams, unsigned _numOutParams);
/// Print the yul syntax to pass input arguments to a function that has
/// @a _numInParams number of input parameters to the output stream.
/// The input arguments are pseudo-randomly chosen from calldata, memory,
/// storage, or the yul optimizer hex dictionary.
/// @param _numInParams Number of input arguments to fill
void fillFunctionCallInput(unsigned _numInParams);
/// Print the yul syntax to save values returned by a function call
/// to the output stream. The values are either stored to memory or
/// storage based on a simulated coin flip. The saved location is
/// decided pseudo-randomly.
/// @param _varsVec A vector of strings that reference variables
/// holding the return values of a function call.
void saveFunctionCallOutput(std::vector<std::string> const& _varsVec);
/// Register a function declaration
/// @param _f Pointer to a FunctionDef object
void registerFunction(FunctionDef const* _f);
/// Removes entry from m_functionMap and m_functionName
void updateFunctionMaps(std::string const& _x);
/// Returns a pseudo-random dictionary token.
/// @param _p Enum that decides if the returned token is hex prefixed ("0x") or not
/// @return Dictionary token at the index computed using a
@@ -144,33 +248,46 @@ private:
return m_counter++;
}
/// Generate function name of the form "foo_<typeSuffix>_<counter>".
/// @param _type Type classified according to the number of
/// values returned by function.
std::string functionName(NumFunctionReturns _type)
{
return "foo_" + functionTypeToString(_type) + "_" + std::to_string(counter());
}
std::ostringstream m_output;
// Number of live variables in inner scope of a function
std::stack<unsigned> m_numVarsPerScope;
// Number of live variables in function scope
unsigned m_numLiveVars;
/// Variables in current scope
std::stack<std::vector<std::string>> m_scopeVars;
/// Functions in current scope
std::stack<std::vector<std::string>> m_scopeFuncs;
/// Variables
std::vector<std::string> m_variables;
/// Functions
std::vector<std::string> m_functions;
/// Maps FunctionDef object to its name
std::map<FunctionDef const*, std::string> m_functionDefMap;
// Set that is used for deduplicating switch case literals
std::stack<std::set<dev::u256>> m_switchLiteralSetPerScope;
// Total number of function sets. A function set contains one function of each type defined by
// NumFunctionReturns
unsigned m_numFunctionSets;
// Look-up table per function type that holds the number of input (output) function parameters
std::vector<unsigned> m_functionVecNoReturnValue;
std::vector<unsigned> m_functionVecSingleReturnValue;
std::vector<std::pair<unsigned, unsigned>> m_functionVecMultiReturnValue;
std::map<std::string, std::pair<unsigned, unsigned>> m_functionSigMap;
// mod input/output parameters impose an upper bound on the number of input/output parameters a function may have.
static unsigned constexpr modInputParams = 5;
static unsigned constexpr modOutputParams = 5;
// predicate to keep track of for body scope
static unsigned constexpr s_modInputParams = 5;
static unsigned constexpr s_modOutputParams = 5;
/// Predicate to keep track of for body scope. If true, break/continue
/// statements can not be created.
bool m_inForBodyScope;
// Index used for naming loop variable of bounded for loops
unsigned m_numNestedForLoops;
// predicate to keep track of for loop init scope
/// Predicate to keep track of for loop init scope. If true, variable
/// or function declarations can not be created.
bool m_inForInitScope;
/// Monotonically increasing counter
unsigned m_counter;
/// Size of protobuf input
unsigned m_inputSize;
/// Predicate that is true if inside function definition, false otherwise
bool m_inFunctionDef;
};
}
}
+75 -83
View File
@@ -21,54 +21,55 @@ message VarDecl {
required Expression expr = 1;
}
message FunctionCallNoReturnVal {
// Indexes a function that does not return anything
required uint32 func_index = 1;
required Expression in_param1 = 2;
required Expression in_param2 = 3;
required Expression in_param3 = 4;
required Expression in_param4 = 5;
}
// Used by Expression
message FunctionCallSingleReturnVal {
// Indexes a function that returns exactly one value
required uint32 func_index = 1;
required Expression in_param1 = 2;
required Expression in_param2 = 3;
required Expression in_param3 = 4;
required Expression in_param4 = 5;
}
message MultiVarDecl {
// Indexes a function that returns more than one value
required uint32 func_index = 1;
required Expression in_param1 = 2;
required Expression in_param2 = 3;
required Expression in_param3 = 4;
required Expression in_param4 = 5;
}
message MultiAssignment {
// Indexes a function that returns more than one value
required uint32 func_index = 1;
required Expression in_param1 = 2;
required Expression in_param2 = 3;
required Expression in_param3 = 4;
required Expression in_param4 = 5;
required VarRef out_param1 = 6;
required VarRef out_param2 = 7;
required VarRef out_param3 = 8;
required VarRef out_param4 = 9;
}
// We exclude function calls with single return value here and use them as expressions
message FunctionCall {
oneof functioncall_oneof {
FunctionCallNoReturnVal call_zero = 1;
MultiVarDecl call_multidecl = 2;
MultiAssignment call_multiassign = 3;
message LowLevelCall {
enum Type {
CALL = 0;
CALLCODE = 1;
DELEGATECALL = 2;
STATICCALL = 3;
}
required Type callty = 1;
required Expression gas = 2;
required Expression addr = 3;
// Valid for call and callcode only
required Expression wei = 4;
required Expression in = 5;
required Expression insize = 6;
required Expression out = 7;
required Expression outsize = 8;
}
message Create {
enum Type {
CREATE = 0;
CREATE2 = 1;
}
required Type createty = 1;
required Expression wei = 2;
required Expression position = 3;
required Expression size = 4;
// Valid for create2 only
required Expression value = 5;
}
message FunctionCall {
enum Returns {
ZERO = 1;
SINGLE = 2;
MULTIDECL = 3;
MULTIASSIGN = 4;
}
required Returns ret = 1;
// Indexes an existing function
required uint32 func_index = 2;
required Expression in_param1 = 3;
required Expression in_param2 = 4;
required Expression in_param3 = 5;
required Expression in_param4 = 6;
required VarRef out_param1 = 7;
required VarRef out_param2 = 8;
required VarRef out_param3 = 9;
required VarRef out_param4 = 10;
}
message TypedVarDecl {
@@ -159,6 +160,8 @@ message UnaryOp {
CALLDATALOAD = 4;
EXTCODESIZE = 5;
EXTCODEHASH = 6;
BALANCE = 7;
BLOCKHASH = 8;
}
required UOp op = 1;
required Expression operand = 2;
@@ -202,6 +205,16 @@ message NullaryOp {
CALLDATASIZE = 4;
CODESIZE = 5;
RETURNDATASIZE = 6;
ADDRESS = 7;
ORIGIN = 8;
CALLER = 9;
CALLVALUE = 10;
GASPRICE = 11;
COINBASE = 12;
TIMESTAMP = 13;
NUMBER = 14;
DIFFICULTY = 15;
GASLIMIT = 16;
}
required NOp op = 1;
}
@@ -242,7 +255,9 @@ message Expression {
UnaryOp unop = 4;
TernaryOp top = 5;
NullaryOp nop = 6;
FunctionCallSingleReturnVal func_expr = 7;
FunctionCall func_expr = 7;
LowLevelCall lowcall = 8;
Create create = 9;
}
}
@@ -311,10 +326,16 @@ message TerminatingStmt {
}
}
// Stub for a VarDecl without an Expression on the RHS
message EmptyVarDecl {}
message FunctionDef {
required uint32 num_input_params = 1;
required uint32 num_output_params = 2;
required Block block = 3;
}
message PopStmt {
required Expression expr = 1;
}
// TODO: Make Function definition a Statement
message Statement {
oneof stmt_oneof {
VarDecl decl = 1;
@@ -332,6 +353,8 @@ message Statement {
TerminatingStmt terminatestmt = 13;
FunctionCall functioncall = 14;
BoundedForStmt boundedforstmt = 15;
FunctionDef funcdef = 16;
PopStmt pop = 17;
}
}
@@ -339,39 +362,8 @@ message Block {
repeated Statement statements = 1;
}
// Identical to Block with the addition of an empty var right at the top
// Used by FunctionDefinitionNoReturnVal only.
message SpecialBlock {
required EmptyVarDecl var = 1;
repeated Statement statements = 2;
}
// This ensures that proto mutator generates at least one of each type if it creates at least 1 functiondef message.
message FunctionDefinition {
required FunctionDefinitionNoReturnVal fd_zero = 1;
required FunctionDefinitionSingleReturnVal fd_one = 2;
required FunctionDefinitionMultiReturnVal fd_multi = 3;
}
// Since this function can have 0 parameters, we hoist an empty var decl at the top via SpecialBlock.
message FunctionDefinitionNoReturnVal {
required uint32 num_input_params = 1;
required SpecialBlock statements = 2;
}
message FunctionDefinitionSingleReturnVal {
required uint32 num_input_params = 1;
required Block statements = 2;
}
message FunctionDefinitionMultiReturnVal {
required uint32 num_input_params = 1;
required uint32 num_output_params = 2;
required Block statements = 3;
}
message Program {
repeated FunctionDefinition funcs = 1;
required Block block = 1;
}
package yul.test.yul_fuzzer;
@@ -250,8 +250,7 @@ u256 EVMInstructionInterpreter::eval(
case Instruction::MLOAD:
if (accessMemory(arg[0], 0x20))
return readMemoryWord(arg[0]);
else
return 0x1234 + arg[0];
return 0;
case Instruction::MSTORE:
if (accessMemory(arg[0], 0x20))
writeMemoryWord(arg[0], arg[1]);
+9 -3
View File
@@ -32,6 +32,7 @@
#include <libyul/optimiser/BlockFlattener.h>
#include <libyul/optimiser/Disambiguator.h>
#include <libyul/optimiser/CallGraphGenerator.h>
#include <libyul/optimiser/CommonSubexpressionEliminator.h>
#include <libyul/optimiser/ControlFlowSimplifier.h>
#include <libyul/optimiser/NameCollector.h>
@@ -54,8 +55,10 @@
#include <libyul/optimiser/SSATransform.h>
#include <libyul/optimiser/StackCompressor.h>
#include <libyul/optimiser/StructuralSimplifier.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/VarDeclInitializer.h>
#include <libyul/optimiser/VarNameCleaner.h>
#include <libyul/optimiser/LoadResolver.h>
#include <libyul/backends/evm/EVMDialect.h>
@@ -133,7 +136,7 @@ public:
cout << " (e)xpr inline/(i)nline/(s)implify/varname c(l)eaner/(u)nusedprune/ss(a) transform/" << endl;
cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-init-rewriter/f(O)r-loop-condition-into-body/" << endl;
cout << " s(t)ructural simplifier/equi(v)alent function combiner/ssa re(V)erser/? " << endl;
cout << " co(n)trol flow simplifier/stack com(p)ressor/(D)ead code eliminator/? " << endl;
cout << " co(n)trol flow simplifier/stack com(p)ressor/(D)ead code eliminator/(L)oad resolver/? " << endl;
cout.flush();
int option = readStandardInputChar();
cout << ' ' << char(option) << endl;
@@ -151,7 +154,7 @@ public:
ForLoopConditionIntoBody{}(*m_ast);
break;
case 'c':
(CommonSubexpressionEliminator{m_dialect})(*m_ast);
CommonSubexpressionEliminator::run(m_dialect, *m_ast);
break;
case 'd':
(VarDeclInitializer{})(*m_ast);
@@ -187,7 +190,7 @@ public:
(ControlFlowSimplifier{m_dialect})(*m_ast);
break;
case 'u':
UnusedPruner::runUntilStabilised(m_dialect, *m_ast);
UnusedPruner::runUntilStabilisedOnFullAST(m_dialect, *m_ast);
break;
case 'D':
DeadCodeEliminator{m_dialect}(*m_ast);
@@ -214,6 +217,9 @@ public:
StackCompressor::run(m_dialect, obj, true, 16);
break;
}
case 'L':
LoadResolver::run(m_dialect, *m_ast);
break;
default:
cout << "Unknown option." << endl;
}