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=f32': stdlib_types.f32_natnum_mul,
|
||||||
'a=f64': stdlib_types.f64_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:
|
def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
||||||
@ -256,26 +272,18 @@ def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType:
|
|||||||
raise NotImplementedError(type3, inp)
|
raise NotImplementedError(type3, inp)
|
||||||
|
|
||||||
U8_OPERATOR_MAP = {
|
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',
|
'^': 'xor',
|
||||||
'|': 'or',
|
'|': 'or',
|
||||||
'&': 'and',
|
'&': 'and',
|
||||||
}
|
}
|
||||||
|
|
||||||
U32_OPERATOR_MAP = {
|
U32_OPERATOR_MAP = {
|
||||||
'<<': 'shl',
|
|
||||||
'>>': 'shr_u',
|
|
||||||
'^': 'xor',
|
'^': 'xor',
|
||||||
'|': 'or',
|
'|': 'or',
|
||||||
'&': 'and',
|
'&': 'and',
|
||||||
}
|
}
|
||||||
|
|
||||||
U64_OPERATOR_MAP = {
|
U64_OPERATOR_MAP = {
|
||||||
'<<': 'shl',
|
|
||||||
'>>': 'shr_u',
|
|
||||||
'^': 'xor',
|
'^': 'xor',
|
||||||
'|': 'or',
|
'|': 'or',
|
||||||
'&': 'and',
|
'&': 'and',
|
||||||
@ -983,6 +991,7 @@ def module(inp: ourlang.Module) -> wasm.Module:
|
|||||||
stdlib_types.__i64_ord_max__,
|
stdlib_types.__i64_ord_max__,
|
||||||
stdlib_types.__i32_intnum_abs__,
|
stdlib_types.__i32_intnum_abs__,
|
||||||
stdlib_types.__i64_intnum_abs__,
|
stdlib_types.__i64_intnum_abs__,
|
||||||
|
stdlib_types.__u32_pow2__,
|
||||||
] + [
|
] + [
|
||||||
function(x)
|
function(x)
|
||||||
for x in inp.functions.values()
|
for x in inp.functions.values()
|
||||||
|
|||||||
@ -211,7 +211,7 @@ def __i32_intnum_abs__(g: Generator, x: i32) -> i32:
|
|||||||
g.i32.sub()
|
g.i32.sub()
|
||||||
g.return_()
|
g.return_()
|
||||||
|
|
||||||
return i32('return')
|
return i32('return') # To satisfy mypy
|
||||||
|
|
||||||
@func_wrapper()
|
@func_wrapper()
|
||||||
def __i64_intnum_abs__(g: Generator, x: i64) -> i64:
|
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.i64.sub()
|
||||||
g.return_()
|
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
|
## class Eq
|
||||||
|
|
||||||
@ -564,6 +582,54 @@ def f32_natnum_mul(g: Generator) -> None:
|
|||||||
def f64_natnum_mul(g: Generator) -> None:
|
def f64_natnum_mul(g: Generator) -> None:
|
||||||
g.add_statement('f64.mul')
|
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
|
## class IntNum
|
||||||
|
|
||||||
|
|||||||
@ -112,6 +112,8 @@ NatNum = Type3Class('NatNum', ['a'], methods={}, operators={
|
|||||||
'+': 'a -> a -> a',
|
'+': 'a -> a -> a',
|
||||||
'-': 'a -> a -> a',
|
'-': '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={
|
IntNum = Type3Class('IntNum', ['a'], methods={
|
||||||
|
|||||||
@ -47,6 +47,9 @@ class Generator_i32i64:
|
|||||||
self.rotl = functools.partial(self.generator.add_statement, f'{prefix}.rotl')
|
self.rotl = functools.partial(self.generator.add_statement, f'{prefix}.rotl')
|
||||||
self.rotr = functools.partial(self.generator.add_statement, f'{prefix}.rotr')
|
self.rotr = functools.partial(self.generator.add_statement, f'{prefix}.rotr')
|
||||||
|
|
||||||
|
# itestop
|
||||||
|
self.eqz = functools.partial(self.generator.add_statement, f'{prefix}.eqz')
|
||||||
|
|
||||||
# irelop
|
# irelop
|
||||||
self.eq = functools.partial(self.generator.add_statement, f'{prefix}.eq')
|
self.eq = functools.partial(self.generator.add_statement, f'{prefix}.eq')
|
||||||
self.ne = functools.partial(self.generator.add_statement, f'{prefix}.ne')
|
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:
|
def __init__(self, generator: 'Generator') -> None:
|
||||||
super().__init__('i64', generator)
|
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:
|
class Generator_f32f64:
|
||||||
def __init__(self, prefix: str, generator: 'Generator') -> None:
|
def __init__(self, prefix: str, generator: 'Generator') -> None:
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
@ -107,6 +114,12 @@ class Generator_f32f64:
|
|||||||
self.le = functools.partial(self.generator.add_statement, f'{prefix}.le')
|
self.le = functools.partial(self.generator.add_statement, f'{prefix}.le')
|
||||||
self.ge = functools.partial(self.generator.add_statement, f'{prefix}.ge')
|
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
|
# 2.4.4. Memory Instructions
|
||||||
self.load = functools.partial(self.generator.add_statement, f'{prefix}.load')
|
self.load = functools.partial(self.generator.add_statement, f'{prefix}.load')
|
||||||
self.store = functools.partial(self.generator.add_statement, f'{prefix}.store')
|
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 4.0 == result.returned_value
|
||||||
assert TYPE_MAP[type_] is type(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.integration_test
|
||||||
@pytest.mark.parametrize('type_', INT_TYPES)
|
@pytest.mark.parametrize('type_', INT_TYPES)
|
||||||
def test_call_with_expression_int(type_):
|
def test_call_with_expression_int(type_):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user