From ad6ca71c53fb91e5c563be2677fc3c23b9a1bf06 Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Fri, 12 Aug 2022 21:50:42 +0200 Subject: [PATCH] More bitwise ops. Steps towards CRC32 --- examples/crc32.py | 9 ++++--- phasm/compiler.py | 7 +++++- phasm/parser.py | 2 ++ tests/integration/test_examples.py | 39 ++++++++++++++++++++++++++++++ tests/integration/test_simple.py | 30 ++++++++++++++++++++++- 5 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 tests/integration/test_examples.py diff --git a/examples/crc32.py b/examples/crc32.py index 87b6912..ff4d2f9 100644 --- a/examples/crc32.py +++ b/examples/crc32.py @@ -13,7 +13,8 @@ # return crc32; # } -_CRC32_Table: [u32] = [ +# Might be the wrong table +_CRC32_Table: u32[256] = [ 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, @@ -80,9 +81,9 @@ _CRC32_Table: [u32] = [ 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, ] -def _crc32_f(val: u32, byt: u8) -> u32: - return (val >> 8) ^ _CRC32_Table[(val ^ byt) & 0xFF] +def _crc32_f(crc: u32, byt: u8) -> u32: + return (crc >> 8) ^ _CRC32_Table[(crc & 0xFF) ^ byt] @exported def crc32(data: bytes) -> u32: - return 0xFFFFFFFF ^ foldli(_crc32_f, 0xFFFFFFFF, data) + return 0xFFFFFFFF ^ foldl(_crc32_f, 0xFFFFFFFF, data) diff --git a/phasm/compiler.py b/phasm/compiler.py index cb640bf..46a9dfe 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -77,10 +77,11 @@ OPERATOR_MAP = { U8_OPERATOR_MAP = { # Under the hood, this is an i32 - # Implementing XOR, OR is fine since the 3 remaining + # Implementing XOR, OR, AND is fine since the 3 remaining # bytes stay zero after this operation '^': 'xor', '|': 'or', + '&': 'and', } U32_OPERATOR_MAP = { @@ -89,6 +90,8 @@ U32_OPERATOR_MAP = { '<=': 'le_u', '>=': 'ge_u', '^': 'xor', + '|': 'or', + '&': 'and', } U64_OPERATOR_MAP = { @@ -97,6 +100,8 @@ U64_OPERATOR_MAP = { '<=': 'le_u', '>=': 'ge_u', '^': 'xor', + '|': 'or', + '&': 'and', } I32_OPERATOR_MAP = { diff --git a/phasm/parser.py b/phasm/parser.py index 2cb1b4a..fdf63c0 100644 --- a/phasm/parser.py +++ b/phasm/parser.py @@ -248,6 +248,8 @@ class OurVisitor: operator = '|' elif isinstance(node.op, ast.BitXor): operator = '^' + elif isinstance(node.op, ast.BitAnd): + operator = '&' else: raise NotImplementedError(f'Operator {node.op}') diff --git a/tests/integration/test_examples.py b/tests/integration/test_examples.py new file mode 100644 index 0000000..b3b278d --- /dev/null +++ b/tests/integration/test_examples.py @@ -0,0 +1,39 @@ +import binascii +import struct + +import pytest + +from .helpers import Suite + +@pytest.mark.integration_test +def test_crc32(): + # FIXME: Stub + # crc = 0xFFFFFFFF + # byt = 0x61 + # => (crc >> 8) ^ _CRC32_Table[(crc & 0xFF) ^ byt] + # (crc >> 8) = 0x00FFFFFF + # => 0x00FFFFFF ^ _CRC32_Table[(crc & 0xFF) ^ byt] + # (crc & 0xFF) = 0xFF + # => 0x00FFFFFF ^ _CRC32_Table[0xFF ^ byt] + # 0xFF ^ 0x61 = 0x9E + # => 0x00FFFFFF ^ _CRC32_Table[0x9E] + # _CRC32_Table[0x9E] = 0x17b7be43 + # => 0x00FFFFFF ^ 0x17b7be43 + + code_py = """ +def _crc32_f(crc: u32, byt: u8) -> u32: + return 16777215 ^ 397917763 + +def testEntry(data: bytes) -> u32: + return 4294967295 ^ _crc32_f(4294967295, data[0]) +""" + exp_result = binascii.crc32(b'a') + + result = Suite(code_py).run_code(b'a') + + # exp_result returns a unsigned integer, as is proper + exp_data = struct.pack('I', exp_result) + # ints extracted from WebAssembly are always signed + data = struct.pack('i', result.returned_value) + + assert exp_data == data diff --git a/tests/integration/test_simple.py b/tests/integration/test_simple.py index 8e30fa0..2669eee 100644 --- a/tests/integration/test_simple.py +++ b/tests/integration/test_simple.py @@ -62,7 +62,21 @@ def testEntry() -> {type_}: @pytest.mark.integration_test @pytest.mark.parametrize('type_', ['u8', 'u32', 'u64']) -def test_xor(type_): +def test_bitwise_or(type_): + code_py = f""" +@exported +def testEntry() -> {type_}: + return 10 | 3 +""" + + result = Suite(code_py).run_code() + + assert 11 == result.returned_value + assert TYPE_MAP[type_] == type(result.returned_value) + +@pytest.mark.integration_test +@pytest.mark.parametrize('type_', ['u8', 'u32', 'u64']) +def test_bitwise_xor(type_): code_py = f""" @exported def testEntry() -> {type_}: @@ -74,6 +88,20 @@ def testEntry() -> {type_}: assert 9 == result.returned_value assert TYPE_MAP[type_] == type(result.returned_value) +@pytest.mark.integration_test +@pytest.mark.parametrize('type_', ['u8', 'u32', 'u64']) +def test_bitwise_and(type_): + code_py = f""" +@exported +def testEntry() -> {type_}: + return 10 & 3 +""" + + result = Suite(code_py).run_code() + + assert 2 == result.returned_value + assert TYPE_MAP[type_] == type(result.returned_value) + @pytest.mark.integration_test @pytest.mark.parametrize('type_', ['f32', 'f64']) def test_buildins_sqrt(type_):