Skip to content

Commit

Permalink
Merge pull request #1796 from iamdefinitelyahuman/bitwise-ops
Browse files Browse the repository at this point in the history
Implement SHL/SHR for bitshifting
  • Loading branch information
fubuloubu authored Jan 2, 2020
2 parents e0e09ad + e3176e7 commit 4ac5ef5
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 15 deletions.
2 changes: 1 addition & 1 deletion docs/compiling-a-contract.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ The following is a list of supported EVM versions, and changes in the compiler i
- ``byzantium``
- The oldest EVM version supported by Vyper.
- ``constantinople``
- The compiler behaves the same way as with byzantium.
- ``shift`` makes use of ``SHL``/``SHR`` opcodes.
- ``petersburg``
- The compiler behaves the same way as with consantinople.
- ``istanbul`` **(default)**
Expand Down
45 changes: 41 additions & 4 deletions tests/parser/functions/test_bitwise.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
def test_test_bitwise(get_contract_with_gas_estimation):
test_bitwise = """
import pytest

from vyper.compiler import (
compile_code,
)
from vyper.opcodes import (
EVM_VERSIONS,
)

code = """
@public
def _bitwise_and(x: uint256, y: uint256) -> uint256:
return bitwise_and(x, y)
Expand All @@ -21,7 +29,21 @@ def _shift(x: uint256, y: int128) -> uint256:
return shift(x, y)
"""

c = get_contract_with_gas_estimation(test_bitwise)

@pytest.mark.parametrize('evm_version', list(EVM_VERSIONS))
def test_bitwise_opcodes(evm_version):
opcodes = compile_code(code, ['opcodes'], evm_version=evm_version)['opcodes']
if evm_version == "byzantium":
assert "SHL" not in opcodes
assert "SHR" not in opcodes
else:
assert "SHL" in opcodes
assert "SHR" in opcodes


@pytest.mark.parametrize('evm_version', list(EVM_VERSIONS))
def test_test_bitwise(get_contract_with_gas_estimation, evm_version):
c = get_contract_with_gas_estimation(code, evm_version=evm_version)
x = 126416208461208640982146408124
y = 7128468721412412459
assert c._bitwise_and(x, y) == (x & y)
Expand All @@ -37,4 +59,19 @@ def _shift(x: uint256, y: int128) -> uint256:
assert c._shift(x, -3) == x // 8
assert c._shift(x, -256) == 0

print("Passed bitwise operation tests")

@pytest.mark.parametrize('evm_version', list(EVM_VERSIONS))
def test_literals(get_contract, evm_version):
code = """
@public
def left(x: uint256) -> uint256:
return shift(x, -3)
@public
def right(x: uint256) -> uint256:
return shift(x, 3)
"""

c = get_contract(code, evm_version=evm_version)
assert c.left(80) == 10
assert c.right(80) == 640
39 changes: 29 additions & 10 deletions vyper/functions/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
StructureException,
TypeMismatchException,
)
from vyper.opcodes import (
version_check,
)
from vyper.parser.expr import (
Expr,
)
Expand Down Expand Up @@ -1063,19 +1066,35 @@ def bitwise_not(expr, args, kwargs, context):

@signature('uint256', 'int128')
def shift(expr, args, kwargs, context):

if args[1].typ.is_literal:
shift_abs = abs(args[1].value)
else:
shift_abs = ['sub', 0, '_s']

if version_check(begin="constantinople"):
left_shift = ['shl', '_s', '_v']
right_shift = ['shr', shift_abs, '_v']
else:
# If second argument is positive, left-shift so multiply by a power of two
# If it is negative, divide by a power of two
# node that if the abs of the second argument >= 256, then in the EVM
# 2**(second arg) = 0, and multiplying OR dividing by 0 gives 0
left_shift = ['mul', '_v', ['exp', 2, '_s']]
right_shift = ['div', '_v', ['exp', 2, shift_abs]]

if not args[1].typ.is_literal:
node_list = ['if', ['slt', '_s', 0], right_shift, left_shift]
elif args[1].value >= 0:
node_list = left_shift
else:
node_list = right_shift

return LLLnode.from_list(
[
'with', '_v', args[0], [
'with', '_s', args[1], [
# If second argument is positive, left-shift so multiply by a power of two
# If it is negative, divide by a power of two
# node that if the abs of the second argument >= 256, then in the EVM
# 2**(second arg) = 0, and multiplying OR dividing by 0 gives 0
'if',
['slt', '_s', 0],
['div', '_v', ['exp', 2, ['sub', 0, '_s']]],
['mul', '_v', ['exp', 2, '_s']]
],
'with', '_s', args[1],
node_list,
],
],
typ=BaseType('uint256'),
Expand Down

0 comments on commit 4ac5ef5

Please sign in to comment.