diff --git a/phasm/compiler.py b/phasm/compiler.py index d7d0cc8..2205941 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -181,6 +181,22 @@ INSTANCES = { 'a=f32': stdlib_types.f32_natnum_mul, 'a=f64': stdlib_types.f64_natnum_mul, }, + type3classes.NatNum.operators['<<']: { + 'a=u32': stdlib_types.u32_natnum_arithmic_shift_left, + 'a=u64': stdlib_types.u64_natnum_arithmic_shift_left, + 'a=i32': stdlib_types.i32_natnum_arithmic_shift_left, + 'a=i64': stdlib_types.i64_natnum_arithmic_shift_left, + 'a=f32': stdlib_types.f32_natnum_arithmic_shift_left, + 'a=f64': stdlib_types.f64_natnum_arithmic_shift_left, + }, + type3classes.NatNum.operators['>>']: { + 'a=u32': stdlib_types.u32_natnum_arithmic_shift_right, + 'a=u64': stdlib_types.u64_natnum_arithmic_shift_right, + 'a=i32': stdlib_types.i32_natnum_arithmic_shift_right, + 'a=i64': stdlib_types.i64_natnum_arithmic_shift_right, + 'a=f32': stdlib_types.f32_natnum_arithmic_shift_right, + 'a=f64': stdlib_types.f64_natnum_arithmic_shift_right, + }, } def phasm_compile(inp: ourlang.Module) -> wasm.Module: @@ -256,26 +272,18 @@ def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType: raise NotImplementedError(type3, inp) U8_OPERATOR_MAP = { - # Under the hood, this is an i32 - # Implementing Right Shift XOR, OR, AND is fine since the 3 remaining - # bytes stay zero after this operation - '>>': 'shr_u', '^': 'xor', '|': 'or', '&': 'and', } U32_OPERATOR_MAP = { - '<<': 'shl', - '>>': 'shr_u', '^': 'xor', '|': 'or', '&': 'and', } U64_OPERATOR_MAP = { - '<<': 'shl', - '>>': 'shr_u', '^': 'xor', '|': 'or', '&': 'and', @@ -983,6 +991,7 @@ def module(inp: ourlang.Module) -> wasm.Module: stdlib_types.__i64_ord_max__, stdlib_types.__i32_intnum_abs__, stdlib_types.__i64_intnum_abs__, + stdlib_types.__u32_pow2__, ] + [ function(x) for x in inp.functions.values() diff --git a/phasm/stdlib/types.py b/phasm/stdlib/types.py index 7f85017..837fa43 100644 --- a/phasm/stdlib/types.py +++ b/phasm/stdlib/types.py @@ -211,7 +211,7 @@ def __i32_intnum_abs__(g: Generator, x: i32) -> i32: g.i32.sub() g.return_() - return i32('return') + return i32('return') # To satisfy mypy @func_wrapper() def __i64_intnum_abs__(g: Generator, x: i64) -> i64: @@ -237,8 +237,26 @@ def __i64_intnum_abs__(g: Generator, x: i64) -> i64: g.i64.sub() g.return_() - return i64('return') + return i64('return') # To satisfy mypy +@func_wrapper() +def __u32_pow2__(g: Generator, x: i32) -> i32: + # 2^0 == 1 + g.local.get(x) + g.i32.eqz() + with g.if_(): + g.i32.const(1) + g.return_() + + # 2 ^ x == 2 << (x - 1) + # (when x > 1) + g.i32.const(2) + g.local.get(x) + g.i32.const(1) + g.i32.sub() + g.i32.shl() + + return i32('return') # To satisfy mypy ## ### ## class Eq @@ -564,6 +582,54 @@ def f32_natnum_mul(g: Generator) -> None: def f64_natnum_mul(g: Generator) -> None: g.add_statement('f64.mul') +def u32_natnum_arithmic_shift_left(g: Generator) -> None: + g.i32.shl() + +def u64_natnum_arithmic_shift_left(g: Generator) -> None: + g.i64.extend_i32_u() + g.i64.shl() + +def i32_natnum_arithmic_shift_left(g: Generator) -> None: + g.i32.shl() + +def i64_natnum_arithmic_shift_left(g: Generator) -> None: + g.i64.extend_i32_u() + g.i64.shl() + +def f32_natnum_arithmic_shift_left(g: Generator) -> None: + g.add_statement('call $stdlib.types.__u32_pow2__') + g.f32.convert_i32_u() + g.f32.mul() + +def f64_natnum_arithmic_shift_left(g: Generator) -> None: + g.add_statement('call $stdlib.types.__u32_pow2__') + g.f64.convert_i32_u() + g.f64.mul() + +def u32_natnum_arithmic_shift_right(g: Generator) -> None: + g.i32.shr_u() + +def u64_natnum_arithmic_shift_right(g: Generator) -> None: + g.i64.extend_i32_u() + g.i64.shr_u() + +def i32_natnum_arithmic_shift_right(g: Generator) -> None: + g.i32.shr_s() + +def i64_natnum_arithmic_shift_right(g: Generator) -> None: + g.i64.extend_i32_u() + g.i64.shr_s() + +def f32_natnum_arithmic_shift_right(g: Generator) -> None: + g.add_statement('call $stdlib.types.__u32_pow2__') + g.f32.convert_i32_u() + g.f32.div() + +def f64_natnum_arithmic_shift_right(g: Generator) -> None: + g.add_statement('call $stdlib.types.__u32_pow2__') + g.f64.convert_i32_u() + g.f64.div() + ## ### ## class IntNum diff --git a/phasm/type3/typeclasses.py b/phasm/type3/typeclasses.py index eae6c6e..519d884 100644 --- a/phasm/type3/typeclasses.py +++ b/phasm/type3/typeclasses.py @@ -112,6 +112,8 @@ NatNum = Type3Class('NatNum', ['a'], methods={}, operators={ '+': 'a -> a -> a', '-': 'a -> a -> a', '*': 'a -> a -> a', + '<<': 'a -> u32 -> a', # Arithmic shift left + '>>': 'a -> u32 -> a', # Arithmic shift right }) IntNum = Type3Class('IntNum', ['a'], methods={ diff --git a/phasm/wasmgenerator.py b/phasm/wasmgenerator.py index da813cc..31ad488 100644 --- a/phasm/wasmgenerator.py +++ b/phasm/wasmgenerator.py @@ -47,6 +47,9 @@ class Generator_i32i64: self.rotl = functools.partial(self.generator.add_statement, f'{prefix}.rotl') self.rotr = functools.partial(self.generator.add_statement, f'{prefix}.rotr') + # itestop + self.eqz = functools.partial(self.generator.add_statement, f'{prefix}.eqz') + # irelop self.eq = functools.partial(self.generator.add_statement, f'{prefix}.eq') self.ne = functools.partial(self.generator.add_statement, f'{prefix}.ne') @@ -75,6 +78,10 @@ class Generator_i64(Generator_i32i64): def __init__(self, generator: 'Generator') -> None: super().__init__('i64', generator) + # 2.4.1. Numeric Instructions + self.extend_i32_s = functools.partial(self.generator.add_statement, 'i64.extend_i32_s') + self.extend_i32_u = functools.partial(self.generator.add_statement, 'i64.extend_i32_u') + class Generator_f32f64: def __init__(self, prefix: str, generator: 'Generator') -> None: self.prefix = prefix @@ -107,6 +114,12 @@ class Generator_f32f64: self.le = functools.partial(self.generator.add_statement, f'{prefix}.le') self.ge = functools.partial(self.generator.add_statement, f'{prefix}.ge') + # Other instr - convert + self.convert_i32_s = functools.partial(self.generator.add_statement, f'{prefix}.convert_i32_s') + self.convert_i32_u = functools.partial(self.generator.add_statement, f'{prefix}.convert_i32_u') + self.convert_i64_s = functools.partial(self.generator.add_statement, f'{prefix}.convert_i64_s') + self.convert_i64_u = functools.partial(self.generator.add_statement, f'{prefix}.convert_i64_u') + # 2.4.4. Memory Instructions self.load = functools.partial(self.generator.add_statement, f'{prefix}.load') self.store = functools.partial(self.generator.add_statement, f'{prefix}.store') diff --git a/tests/integration/test_lang/test_natnum.py b/tests/integration/test_lang/test_natnum.py index 7740cf7..1649787 100644 --- a/tests/integration/test_lang/test_natnum.py +++ b/tests/integration/test_lang/test_natnum.py @@ -138,6 +138,62 @@ def testEntry() -> {type_}: assert 4.0 == result.returned_value assert TYPE_MAP[type_] is type(result.returned_value) +@pytest.mark.integration_test +@pytest.mark.parametrize('type_', INT_TYPES) +def test_arithmic_shift_right_int(type_): + code_py = f""" +@exported +def testEntry() -> {type_}: + return 100 >> 3 +""" + + result = Suite(code_py).run_code() + + assert 12 == result.returned_value + assert TYPE_MAP[type_] is type(result.returned_value) + +@pytest.mark.integration_test +@pytest.mark.parametrize('type_', FLOAT_TYPES) +def test_arithmic_shift_right_float(type_): + code_py = f""" +@exported +def testEntry() -> {type_}: + return 100.0 >> 3 +""" + + result = Suite(code_py).run_code() + + assert 12.5 == result.returned_value + assert TYPE_MAP[type_] is type(result.returned_value) + +@pytest.mark.integration_test +@pytest.mark.parametrize('type_', INT_TYPES) +def test_arithmic_shift_left_int(type_): + code_py = f""" +@exported +def testEntry() -> {type_}: + return 3 << 3 +""" + + result = Suite(code_py).run_code() + + assert 24 == result.returned_value + assert TYPE_MAP[type_] is type(result.returned_value) + +@pytest.mark.integration_test +@pytest.mark.parametrize('type_', FLOAT_TYPES) +def test_arithmic_shift_left_float(type_): + code_py = f""" +@exported +def testEntry() -> {type_}: + return 3.5 << 3 +""" + + result = Suite(code_py).run_code() + + assert 28.0 == result.returned_value + assert TYPE_MAP[type_] is type(result.returned_value) + @pytest.mark.integration_test @pytest.mark.parametrize('type_', INT_TYPES) def test_call_with_expression_int(type_):