Adds arithmetic shift shift
"An arithmetic shift is usually equivalent to multiplying the number by a positive or a negative integral power of the radix." -- Federal Standard 1037C "This is the same as multiplying x by 2**y." "A right shift by n bits is defined as floor division by pow(2,n). A left shift by n bits is defined as multiplication with pow(2,n)."
This commit is contained in:
parent
da381e4a48
commit
99e407c3c0
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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={
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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_):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user