mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #6939 from ethereum/opt_proofs
Move optimization proofs repo to Solidity repo
This commit is contained in:
commit
4aa0c9e079
6
test/formal/README.md
Normal file
6
test/formal/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
The Solidity compiler implements several [optimization rules](https://github.com/ethereum/solidity/blob/develop/libevmasm/RuleList.h).
|
||||
|
||||
This directory contains an effort to formally prove the correctness of those rules in:
|
||||
|
||||
- HOL with [EthIsabelle](https://github.com/ekpyron/eth-isabelle)
|
||||
- FOL with SMT solvers using [Integers and BitVectors](http://smtlib.cs.uiowa.edu/theories.shtml)
|
27
test/formal/combine_div_shl_one_32.py
Normal file
27
test/formal/combine_div_shl_one_32.py
Normal file
@ -0,0 +1,27 @@
|
||||
from rule import Rule
|
||||
from opcodes import *
|
||||
|
||||
"""
|
||||
Rule:
|
||||
DIV(X, SHL(Y, 1)) -> SHR(Y, X)
|
||||
Requirements:
|
||||
"""
|
||||
|
||||
rule = Rule()
|
||||
|
||||
n_bits = 32
|
||||
|
||||
# Input vars
|
||||
X = BitVec('X', n_bits)
|
||||
Y = BitVec('Y', n_bits)
|
||||
|
||||
# Constants
|
||||
ONE = BitVecVal(1, n_bits)
|
||||
|
||||
# Non optimized result
|
||||
nonopt = DIV(X, SHL(Y, ONE))
|
||||
|
||||
# Optimized result
|
||||
opt = SHR(Y, X)
|
||||
|
||||
rule.check(nonopt, opt)
|
33
test/formal/combine_mul_shl_one_64.py
Normal file
33
test/formal/combine_mul_shl_one_64.py
Normal file
@ -0,0 +1,33 @@
|
||||
from rule import Rule
|
||||
from opcodes import *
|
||||
|
||||
"""
|
||||
Rule:
|
||||
MUL(X, SHL(Y, 1)) -> SHL(Y, X)
|
||||
MUL(SHL(X, 1), Y) -> SHL(X, Y)
|
||||
Requirements:
|
||||
"""
|
||||
|
||||
rule = Rule()
|
||||
|
||||
n_bits = 64
|
||||
|
||||
# Input vars
|
||||
X = BitVec('X', n_bits)
|
||||
Y = BitVec('Y', n_bits)
|
||||
|
||||
# Constants
|
||||
ONE = BitVecVal(1, n_bits)
|
||||
|
||||
# Requirements
|
||||
|
||||
# Non optimized result
|
||||
nonopt_1 = MUL(X, SHL(Y, ONE))
|
||||
nonopt_2 = MUL(SHL(X, ONE), Y)
|
||||
|
||||
# Optimized result
|
||||
opt_1 = SHL(Y, X)
|
||||
opt_2 = SHL(X, Y)
|
||||
|
||||
rule.check(nonopt_1, opt_1)
|
||||
rule.check(nonopt_2, opt_2)
|
44
test/formal/combine_shl_shr_by_constant_64.py
Normal file
44
test/formal/combine_shl_shr_by_constant_64.py
Normal file
@ -0,0 +1,44 @@
|
||||
from rule import Rule
|
||||
from opcodes import *
|
||||
|
||||
"""
|
||||
Rule:
|
||||
mask = shlWorkaround(u256(-1), unsigned(A.d())) >> unsigned(B.d())
|
||||
SHR(B, SHL(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
|
||||
Requirements:
|
||||
A < BitWidth
|
||||
B < BitWidth
|
||||
"""
|
||||
|
||||
rule = Rule()
|
||||
|
||||
n_bits = 64
|
||||
|
||||
# Input vars
|
||||
X = BitVec('X', n_bits)
|
||||
A = BitVec('A', n_bits)
|
||||
B = BitVec('B', n_bits)
|
||||
|
||||
# Constants
|
||||
BitWidth = BitVecVal(n_bits, n_bits)
|
||||
|
||||
# Requirements
|
||||
rule.require(ULT(A, BitWidth))
|
||||
rule.require(ULT(B, BitWidth))
|
||||
|
||||
# Non optimized result
|
||||
nonopt = SHR(B, SHL(A, X))
|
||||
|
||||
# Optimized result
|
||||
Mask = SHR(B, SHL(A, Int2BV(IntVal(-1), n_bits)))
|
||||
opt = If(
|
||||
UGT(A, B),
|
||||
AND(SHL(A - B, X), Mask),
|
||||
If(
|
||||
UGT(B, A),
|
||||
AND(SHR(B - A, X), Mask),
|
||||
AND(X, Mask)
|
||||
)
|
||||
)
|
||||
|
||||
rule.check(nonopt, opt)
|
44
test/formal/combine_shr_shl_by_constant_64.py
Normal file
44
test/formal/combine_shr_shl_by_constant_64.py
Normal file
@ -0,0 +1,44 @@
|
||||
from rule import Rule
|
||||
from opcodes import *
|
||||
|
||||
"""
|
||||
Rule:
|
||||
mask = shlWorkaround(u256(-1) >> unsigned(A.d()), unsigned(B.d()))
|
||||
SHL(B, SHR(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
|
||||
Requirements:
|
||||
A < BitWidth
|
||||
B < BitWidth
|
||||
"""
|
||||
|
||||
rule = Rule()
|
||||
|
||||
n_bits = 64
|
||||
|
||||
# Input vars
|
||||
X = BitVec('X', n_bits)
|
||||
A = BitVec('A', n_bits)
|
||||
B = BitVec('B', n_bits)
|
||||
|
||||
# Constants
|
||||
BitWidth = BitVecVal(n_bits, n_bits)
|
||||
|
||||
# Requirements
|
||||
rule.require(ULT(A, BitWidth))
|
||||
rule.require(ULT(B, BitWidth))
|
||||
|
||||
# Non optimized result
|
||||
nonopt = SHL(B, SHR(A, X))
|
||||
|
||||
# Optimized result
|
||||
Mask = SHL(B, SHR(A, Int2BV(IntVal(-1), n_bits)))
|
||||
opt = If(
|
||||
UGT(A, B),
|
||||
AND(SHR(a - b, x), Mask),
|
||||
If(
|
||||
UGT(B, A),
|
||||
AND(SHL(B - A, X), Mask),
|
||||
AND(X, Mask)
|
||||
)
|
||||
)
|
||||
|
||||
rule.check(nonopt, opt)
|
36
test/formal/move_and_across_shl_128.py
Normal file
36
test/formal/move_and_across_shl_128.py
Normal file
@ -0,0 +1,36 @@
|
||||
from rule import Rule
|
||||
from opcodes import *
|
||||
|
||||
"""
|
||||
Rule:
|
||||
SHL(B, AND(X, A)) -> AND(SHL(B, X), A << B)
|
||||
SHL(B, AND(A, X)) -> AND(SHL(B, X), A << B)
|
||||
Requirements:
|
||||
B < BitWidth
|
||||
"""
|
||||
|
||||
rule = Rule()
|
||||
|
||||
n_bits = 128
|
||||
|
||||
# Input vars
|
||||
X = BitVec('X', n_bits)
|
||||
A = BitVec('A', n_bits)
|
||||
B = BitVec('B', n_bits)
|
||||
|
||||
# Constants
|
||||
BitWidth = BitVecVal(n_bits, n_bits)
|
||||
|
||||
# Requirements
|
||||
rule.require(ULT(B, BitWidth))
|
||||
|
||||
# Non optimized result
|
||||
nonopt_1 = SHL(B, AND(X, A))
|
||||
nonopt_2 = SHL(B, AND(A, X))
|
||||
|
||||
# Optimized result
|
||||
Mask = SHL(B, A)
|
||||
opt = AND(SHL(B, X), Mask)
|
||||
|
||||
rule.check(nonopt_1, opt)
|
||||
rule.check(nonopt_2, opt)
|
36
test/formal/move_and_across_shr_128.py
Normal file
36
test/formal/move_and_across_shr_128.py
Normal file
@ -0,0 +1,36 @@
|
||||
from rule import Rule
|
||||
from opcodes import *
|
||||
|
||||
"""
|
||||
Rule:
|
||||
SHR(B, AND(X, A)) -> AND(SHR(B, X), A >> B)
|
||||
SHR(B, AND(A, X)) -> AND(SHR(B, X), A >> B)
|
||||
Requirements:
|
||||
B < BitWidth
|
||||
"""
|
||||
|
||||
rule = Rule()
|
||||
|
||||
n_bits = 128
|
||||
|
||||
# Input vars
|
||||
X = BitVec('X', n_bits)
|
||||
A = BitVec('A', n_bits)
|
||||
B = BitVec('B', n_bits)
|
||||
|
||||
# Constants
|
||||
BitWidth = BitVecVal(n_bits, n_bits)
|
||||
|
||||
# Requirements
|
||||
rule.require(ULT(B, BitWidth))
|
||||
|
||||
# Non optimized result
|
||||
nonopt_1 = SHR(B, AND(X, A));
|
||||
nonopt_2 = SHR(B, AND(A, X));
|
||||
|
||||
# Optimized result
|
||||
Mask = SHR(B, A);
|
||||
opt = AND(SHR(B, X), Mask);
|
||||
|
||||
rule.check(nonopt_1, opt)
|
||||
rule.check(nonopt_2, opt)
|
46
test/formal/opcodes.py
Normal file
46
test/formal/opcodes.py
Normal file
@ -0,0 +1,46 @@
|
||||
from z3 import *
|
||||
|
||||
def ADD(x, y):
|
||||
return x + y
|
||||
|
||||
def MUL(x, y):
|
||||
return x * y
|
||||
|
||||
def SUB(x, y):
|
||||
return x - y
|
||||
|
||||
def DIV(x, y):
|
||||
return If(y == 0, 0, UDiv(x, y))
|
||||
|
||||
def SDIV(x, y):
|
||||
return If(y == 0, 0, x / y)
|
||||
|
||||
def MOD(x, y):
|
||||
return If(y == 0, 0, URem(x, y))
|
||||
|
||||
def SMOD(x, y):
|
||||
return If(y == 0, 0, x % y)
|
||||
|
||||
def LT(x, y):
|
||||
return ULT(x, y)
|
||||
|
||||
def GT(x, y):
|
||||
return UGT(x, y)
|
||||
|
||||
def SLT(x, y):
|
||||
return x < y
|
||||
|
||||
def SGT(x, y):
|
||||
return x > y
|
||||
|
||||
def AND(x, y):
|
||||
return x & y
|
||||
|
||||
def SHL(x, y):
|
||||
return y << x
|
||||
|
||||
def SHR(x, y):
|
||||
return LShR(y, x)
|
||||
|
||||
def SAR(x, y):
|
||||
return y >> x
|
38
test/formal/rule.py
Normal file
38
test/formal/rule.py
Normal file
@ -0,0 +1,38 @@
|
||||
from z3 import *
|
||||
|
||||
class Rule:
|
||||
def __init__(self):
|
||||
self.requirements = []
|
||||
self.constraints = []
|
||||
self.solver = Solver()
|
||||
self.setTimeout(60000)
|
||||
|
||||
def setTimeout(self, _t):
|
||||
self.solver.set("timeout", _t)
|
||||
|
||||
def __lshift__(self, _c):
|
||||
self.constraints.append(_c)
|
||||
|
||||
def require(self, _r):
|
||||
self.requirements.append(_r)
|
||||
|
||||
def check(self, _nonopt, _opt):
|
||||
self.solver.add(self.requirements)
|
||||
result = self.solver.check()
|
||||
|
||||
if result == unknown:
|
||||
raise BaseException('Unable to satisfy requirements.')
|
||||
elif result == unsat:
|
||||
raise BaseException('Requirements are unsatisfiable.')
|
||||
|
||||
self.solver.push()
|
||||
self.solver.add(self.constraints)
|
||||
self.solver.add(_nonopt != _opt)
|
||||
|
||||
result = self.solver.check()
|
||||
if result == unknown:
|
||||
raise BaseException('Unable to prove rule.')
|
||||
elif result == sat:
|
||||
m = self.solver.model()
|
||||
raise BaseException('Rule is incorrect.\nModel: ' + str(m))
|
||||
self.solver.pop()
|
27
test/formal/shl_workaround_8.py
Normal file
27
test/formal/shl_workaround_8.py
Normal file
@ -0,0 +1,27 @@
|
||||
from rule import Rule
|
||||
from opcodes import *
|
||||
|
||||
"""
|
||||
Shift left workaround that Solidity implements
|
||||
due to a bug in Boost.
|
||||
"""
|
||||
|
||||
rule = Rule()
|
||||
|
||||
n_bits = 8
|
||||
bigint_bits = 16
|
||||
|
||||
# Input vars
|
||||
X = BitVec('X', n_bits)
|
||||
A = BitVec('A', n_bits)
|
||||
B = BitVec('B', bigint_bits)
|
||||
|
||||
# Compute workaround
|
||||
workaround = Int2BV(
|
||||
BV2Int(
|
||||
(Int2BV(BV2Int(X), bigint_bits) << Int2BV(BV2Int(A), bigint_bits)) &
|
||||
Int2BV(BV2Int(Int2BV(IntVal(-1), n_bits)), bigint_bits)
|
||||
), n_bits
|
||||
)
|
||||
|
||||
rule.check(workaround, SHL(A, X))
|
Loading…
Reference in New Issue
Block a user