Implements ceil, floor, trunc, nearest
To round of the f32 / f64 wasm supported opcodes. This also means we can remove the now outdated WEBASSEMBLY_BUILTIN_FLOAT_OPS.
This commit is contained in:
parent
9bc8d94ffd
commit
46dbc90475
6
TODO.md
6
TODO.md
@ -13,6 +13,7 @@
|
||||
- Allocation is done using pointers for members, is this desired?
|
||||
- Functions don't seem to be a thing on typing level yet?
|
||||
- static_array and tuple should probably not be PrimitiveType3, but instead subclass AppliedType3?
|
||||
- See if we want to replace Fractional with Real, and add Rational, Irrationl, Algebraic, Transendental
|
||||
|
||||
- test_bitwise_or_inv_type
|
||||
- test_bytes_index_out_of_bounds vs static trap(?)
|
||||
@ -22,8 +23,9 @@
|
||||
- Either there should be more of them or less
|
||||
- At first glance, looks like failure in the typing system
|
||||
- Related to the FIXME in phasm_type3?
|
||||
- WEBASSEMBLY_BUILTIN_FLOAT_OPS and WEBASSEMBLY_BUILTIN_BYTES_OPS are special cased
|
||||
- Should be part of a prelude
|
||||
- WEBASSEMBLY_BUILTIN_BYTES_OPS is special cased
|
||||
- Should be part of a prelude (?)
|
||||
- In Haskell this is not a type class
|
||||
- Casting is not implemented except u32 which is special cased
|
||||
- Parser is putting stuff in ModuleDataBlock
|
||||
- Compiler should probably do that
|
||||
|
||||
@ -90,8 +90,7 @@ def expression(inp: ourlang.Expression) -> str:
|
||||
|
||||
if isinstance(inp, ourlang.UnaryOp):
|
||||
if (
|
||||
inp.operator in ourlang.WEBASSEMBLY_BUILTIN_FLOAT_OPS
|
||||
or inp.operator in ourlang.WEBASSEMBLY_BUILTIN_BYTES_OPS):
|
||||
inp.operator in ourlang.WEBASSEMBLY_BUILTIN_BYTES_OPS):
|
||||
return f'{inp.operator}({expression(inp.right)})'
|
||||
|
||||
if inp.operator == 'cast':
|
||||
|
||||
@ -53,6 +53,22 @@ INSTANCES = {
|
||||
'a=f32': stdlib_types.f32_floating_sqrt,
|
||||
'a=f64': stdlib_types.f64_floating_sqrt,
|
||||
},
|
||||
type3classes.Fractional.methods['ceil']: {
|
||||
'a=f32': stdlib_types.f32_fractional_ceil,
|
||||
'a=f64': stdlib_types.f64_fractional_ceil,
|
||||
},
|
||||
type3classes.Fractional.methods['floor']: {
|
||||
'a=f32': stdlib_types.f32_fractional_floor,
|
||||
'a=f64': stdlib_types.f64_fractional_floor,
|
||||
},
|
||||
type3classes.Fractional.methods['trunc']: {
|
||||
'a=f32': stdlib_types.f32_fractional_trunc,
|
||||
'a=f64': stdlib_types.f64_fractional_trunc,
|
||||
},
|
||||
type3classes.Fractional.methods['nearest']: {
|
||||
'a=f32': stdlib_types.f32_fractional_nearest,
|
||||
'a=f64': stdlib_types.f64_fractional_nearest,
|
||||
},
|
||||
type3classes.Fractional.operators['/']: {
|
||||
'a=f32': stdlib_types.f32_fractional_div,
|
||||
'a=f64': stdlib_types.f64_fractional_div,
|
||||
@ -429,15 +445,6 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
|
||||
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
|
||||
|
||||
if inp.type3 == type3types.f32:
|
||||
if inp.operator in ourlang.WEBASSEMBLY_BUILTIN_FLOAT_OPS:
|
||||
wgn.add_statement(f'f32.{inp.operator}')
|
||||
return
|
||||
if inp.type3 == type3types.f64:
|
||||
if inp.operator in ourlang.WEBASSEMBLY_BUILTIN_FLOAT_OPS:
|
||||
wgn.add_statement(f'f64.{inp.operator}')
|
||||
return
|
||||
|
||||
if inp.type3 == type3types.u32:
|
||||
if inp.operator == 'len':
|
||||
if inp.right.type3 == type3types.bytes:
|
||||
|
||||
@ -10,7 +10,6 @@ from .type3 import typeclasses as type3typeclasses
|
||||
from .type3 import types as type3types
|
||||
from .type3.types import PlaceholderForType, StructType3, Type3, Type3OrPlaceholder
|
||||
|
||||
WEBASSEMBLY_BUILTIN_FLOAT_OPS: Final = ('abs', 'ceil', 'floor', 'trunc', 'nearest', )
|
||||
WEBASSEMBLY_BUILTIN_BYTES_OPS: Final = ('len', )
|
||||
|
||||
class Expression:
|
||||
|
||||
@ -6,7 +6,6 @@ from typing import Any, Dict, NoReturn, Union
|
||||
|
||||
from .exceptions import StaticError
|
||||
from .ourlang import (
|
||||
WEBASSEMBLY_BUILTIN_FLOAT_OPS,
|
||||
AccessStructMember,
|
||||
BinaryOp,
|
||||
ConstantBytes,
|
||||
@ -490,14 +489,6 @@ class OurVisitor:
|
||||
# FIXME: Defer struct de-allocation
|
||||
|
||||
func = module.functions[struct_constructor.name]
|
||||
elif node.func.id in WEBASSEMBLY_BUILTIN_FLOAT_OPS:
|
||||
if 1 != len(node.args):
|
||||
_raise_static_error(node, f'Function {node.func.id} requires 1 arguments but {len(node.args)} are given')
|
||||
|
||||
return UnaryOp(
|
||||
'sqrt',
|
||||
self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[0]),
|
||||
)
|
||||
elif node.func.id == 'u32':
|
||||
if 1 != len(node.args):
|
||||
_raise_static_error(node, f'Function {node.func.id} requires 1 arguments but {len(node.args)} are given')
|
||||
|
||||
@ -174,11 +174,38 @@ def f64_eq_not_equals(g: Generator) -> None:
|
||||
## ###
|
||||
## class Fractional
|
||||
|
||||
def f32_fractional_ceil(g: Generator) -> None:
|
||||
g.f32.ceil()
|
||||
|
||||
def f64_fractional_ceil(g: Generator) -> None:
|
||||
g.f64.ceil()
|
||||
|
||||
def f32_fractional_floor(g: Generator) -> None:
|
||||
g.f32.floor()
|
||||
|
||||
def f64_fractional_floor(g: Generator) -> None:
|
||||
g.f64.floor()
|
||||
|
||||
def f32_fractional_trunc(g: Generator) -> None:
|
||||
g.f32.trunc()
|
||||
|
||||
def f64_fractional_trunc(g: Generator) -> None:
|
||||
g.f64.trunc()
|
||||
|
||||
def f32_fractional_nearest(g: Generator) -> None:
|
||||
g.f32.nearest()
|
||||
|
||||
def f64_fractional_nearest(g: Generator) -> None:
|
||||
g.f64.nearest()
|
||||
|
||||
def f32_fractional_div(g: Generator) -> None:
|
||||
g.add_statement('f32.div')
|
||||
g.f32.div()
|
||||
|
||||
def f64_fractional_div(g: Generator) -> None:
|
||||
g.add_statement('f64.div')
|
||||
g.f64.div()
|
||||
|
||||
## ###
|
||||
## class Floating
|
||||
|
||||
def f32_floating_sqrt(g: Generator) -> None:
|
||||
g.add_statement('f32.sqrt')
|
||||
|
||||
@ -113,7 +113,12 @@ Integral = Type3Class('Eq', ['a'], methods={
|
||||
'div': 'a -> a -> a',
|
||||
}, operators={}, inherited_classes=[NatNum])
|
||||
|
||||
Fractional = Type3Class('Fractional', ['a'], methods={}, operators={
|
||||
Fractional = Type3Class('Fractional', ['a'], methods={
|
||||
'ceil': 'a -> a',
|
||||
'floor': 'a -> a',
|
||||
'trunc': 'a -> a',
|
||||
'nearest': 'a -> a',
|
||||
}, operators={
|
||||
'/': 'a -> a -> a',
|
||||
}, inherited_classes=[NatNum])
|
||||
|
||||
|
||||
@ -4,44 +4,70 @@ from ..helpers import Suite
|
||||
|
||||
TYPE_LIST = ['f32', 'f64']
|
||||
|
||||
TEST_LIST = [
|
||||
('10.0 / 8.0', 1.25, ),
|
||||
|
||||
# WebAssembly dictates that float division follows the IEEE rules
|
||||
# https://www.w3.org/TR/wasm-core-1/#-hrefop-fdivmathrmfdiv_n-z_1-z_2
|
||||
('10.0 / 0.0', float('+inf') , ),
|
||||
('-10.0 / 0.0', float('-inf') , ),
|
||||
|
||||
( 'ceil(4.5)', 5.0, ),
|
||||
( 'ceil(4.75)', 5.0, ),
|
||||
( 'ceil(5.0)', 5.0, ),
|
||||
( 'ceil(5.25)', 6.0, ),
|
||||
( 'ceil(5.5)', 6.0, ),
|
||||
('ceil(-4.5)', -4.0, ),
|
||||
('ceil(-4.75)', -4.0, ),
|
||||
('ceil(-5.0)' , -5.0, ),
|
||||
('ceil(-5.25)', -5.0, ),
|
||||
('ceil(-5.5)', -5.0, ),
|
||||
|
||||
( 'floor(4.5)', 4.0, ),
|
||||
( 'floor(4.75)', 4.0, ),
|
||||
( 'floor(5.0)', 5.0, ),
|
||||
( 'floor(5.25)', 5.0, ),
|
||||
( 'floor(5.5)', 5.0, ),
|
||||
('floor(-4.5)', -5.0, ),
|
||||
('floor(-4.75)', -5.0, ),
|
||||
('floor(-5.0)' , -5.0, ),
|
||||
('floor(-5.25)', -6.0, ),
|
||||
('floor(-5.5)', -6.0, ),
|
||||
|
||||
( 'trunc(4.5)', 4.0, ),
|
||||
( 'trunc(4.75)', 4.0, ),
|
||||
( 'trunc(5.0)', 5.0, ),
|
||||
( 'trunc(5.25)', 5.0, ),
|
||||
( 'trunc(5.5)', 5.0, ),
|
||||
('trunc(-4.5)', -4.0, ),
|
||||
('trunc(-4.75)', -4.0, ),
|
||||
('trunc(-5.0)' , -5.0, ),
|
||||
('trunc(-5.25)', -5.0, ),
|
||||
('trunc(-5.5)', -5.0, ),
|
||||
|
||||
( 'nearest(4.5)', 4.0, ),
|
||||
( 'nearest(4.75)', 5.0, ),
|
||||
( 'nearest(5.0)', 5.0, ),
|
||||
( 'nearest(5.25)', 5.0, ),
|
||||
( 'nearest(5.5)', 6.0, ),
|
||||
('nearest(-4.5)', -4.0, ),
|
||||
('nearest(-4.75)', -5.0, ),
|
||||
('nearest(-5.0)', -5.0, ),
|
||||
('nearest(-5.25)', -5.0, ),
|
||||
('nearest(-5.5)', -6.0, ),
|
||||
]
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', TYPE_LIST)
|
||||
def test_division_float(type_):
|
||||
@pytest.mark.parametrize('test_in,test_out', TEST_LIST)
|
||||
def test_fractional(type_, test_in, test_out):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry() -> {type_}:
|
||||
return 10.0 / 8.0
|
||||
return {test_in}
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 1.25 == result.returned_value
|
||||
assert test_out == result.returned_value
|
||||
assert isinstance(result.returned_value, float)
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', TYPE_LIST)
|
||||
def test_division_float_follow_ieee_so_inf_pos(type_):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry() -> {type_}:
|
||||
return 10.0 / 0.0
|
||||
"""
|
||||
|
||||
# WebAssembly dictates that float division follows the IEEE rules
|
||||
# https://www.w3.org/TR/wasm-core-1/#-hrefop-fdivmathrmfdiv_n-z_1-z_2
|
||||
result = Suite(code_py).run_code()
|
||||
assert float('+inf') == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', TYPE_LIST)
|
||||
def test_division_float_follow_ieee_so_inf_neg(type_):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry() -> {type_}:
|
||||
return -10.0 / 0.0
|
||||
"""
|
||||
|
||||
# WebAssembly dictates that float division follows the IEEE rules
|
||||
# https://www.w3.org/TR/wasm-core-1/#-hrefop-fdivmathrmfdiv_n-z_1-z_2
|
||||
result = Suite(code_py).run_code()
|
||||
assert float('-inf') == result.returned_value
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user