From 56ab88db2cea20c0997d14b33df8561aa5e96cb8 Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Sun, 25 May 2025 13:45:18 +0200 Subject: [PATCH] Exposes Wasm's reinterpret function --- phasm/prelude/__init__.py | 30 +++++++++++++ phasm/stdlib/types.py | 35 +++++++++++++++ phasm/wasmgenerator.py | 5 +++ .../test_typeclasses/test_reinterpretable.py | 44 +++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 tests/integration/test_typeclasses/test_reinterpretable.py diff --git a/phasm/prelude/__init__.py b/phasm/prelude/__init__.py index 5c5afd8..23402dd 100644 --- a/phasm/prelude/__init__.py +++ b/phasm/prelude/__init__.py @@ -551,6 +551,35 @@ instance_type_class(Promotable, f32, f64, methods={ 'demote': stdtypes.f32_f64_demote, }) +Reinterpretable = Type3Class('Reinterpretable', (a, b, ), methods={ + 'reinterpret': [a, b] +}, operators={}) + +instance_type_class(Reinterpretable, u32, f32, methods={ + 'reinterpret': stdtypes.u32_f32_reinterpret, +}) +instance_type_class(Reinterpretable, u64, f64, methods={ + 'reinterpret': stdtypes.u64_f64_reinterpret, +}) +instance_type_class(Reinterpretable, i32, f32, methods={ + 'reinterpret': stdtypes.i32_f32_reinterpret, +}) +instance_type_class(Reinterpretable, i64, f64, methods={ + 'reinterpret': stdtypes.i64_f64_reinterpret, +}) +instance_type_class(Reinterpretable, f32, u32, methods={ + 'reinterpret': stdtypes.f32_u32_reinterpret, +}) +instance_type_class(Reinterpretable, f64, u64, methods={ + 'reinterpret': stdtypes.f64_u64_reinterpret, +}) +instance_type_class(Reinterpretable, f32, i32, methods={ + 'reinterpret': stdtypes.f32_i32_reinterpret, +}) +instance_type_class(Reinterpretable, f64, i64, methods={ + 'reinterpret': stdtypes.f64_i64_reinterpret, +}) + Foldable = Type3Class('Foldable', (t, ), methods={ 'sum': [t(a), a], 'foldl': [[b, a, b], b, t(a), b], @@ -621,5 +650,6 @@ PRELUDE_METHODS = { **Sized_.methods, **Extendable.methods, **Promotable.methods, + **Reinterpretable.methods, **Foldable.methods, } diff --git a/phasm/stdlib/types.py b/phasm/stdlib/types.py index 02f5519..a6621b4 100644 --- a/phasm/stdlib/types.py +++ b/phasm/stdlib/types.py @@ -1141,6 +1141,41 @@ def f32_f64_demote(g: Generator, tv_map: TypeVariableLookup) -> None: del tv_map g.f32.demote_f64() +## ### +## Reinterpretable + +def i32_f32_reinterpret(g: Generator, tv_map: TypeVariableLookup) -> None: + del tv_map + g.f32.reinterpret_i32() + +def u32_f32_reinterpret(g: Generator, tv_map: TypeVariableLookup) -> None: + del tv_map + g.f32.reinterpret_i32() + +def i64_f64_reinterpret(g: Generator, tv_map: TypeVariableLookup) -> None: + del tv_map + g.f64.reinterpret_i64() + +def u64_f64_reinterpret(g: Generator, tv_map: TypeVariableLookup) -> None: + del tv_map + g.f64.reinterpret_i64() + +def f32_i32_reinterpret(g: Generator, tv_map: TypeVariableLookup) -> None: + del tv_map + g.i32.reinterpret_f32() + +def f32_u32_reinterpret(g: Generator, tv_map: TypeVariableLookup) -> None: + del tv_map + g.i32.reinterpret_f32() + +def f64_i64_reinterpret(g: Generator, tv_map: TypeVariableLookup) -> None: + del tv_map + g.i64.reinterpret_f64() + +def f64_u64_reinterpret(g: Generator, tv_map: TypeVariableLookup) -> None: + del tv_map + g.i64.reinterpret_f64() + ## ### ## Foldable diff --git a/phasm/wasmgenerator.py b/phasm/wasmgenerator.py index 9028325..47bebc8 100644 --- a/phasm/wasmgenerator.py +++ b/phasm/wasmgenerator.py @@ -81,6 +81,7 @@ class Generator_i32(Generator_i32i64): super().__init__('i32', generator) # 2.4.1. Numeric Instructions + self.reinterpret_f32 = functools.partial(self.generator.add_statement, 'i32.reinterpret_f32') self.wrap_i64 = functools.partial(self.generator.add_statement, 'i32.wrap_i64') class Generator_i64(Generator_i32i64): @@ -90,6 +91,7 @@ class Generator_i64(Generator_i32i64): # 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') + self.reinterpret_f64 = functools.partial(self.generator.add_statement, 'i64.reinterpret_f64') class Generator_f32f64: def __init__(self, prefix: str, generator: 'Generator') -> None: @@ -143,6 +145,7 @@ class Generator_f32(Generator_f32f64): # 2.4.1 Numeric Instructions self.demote_f64 = functools.partial(self.generator.add_statement, 'f32.demote_f64') + self.reinterpret_i32 = functools.partial(self.generator.add_statement, 'f32.reinterpret_i32') class Generator_f64(Generator_f32f64): def __init__(self, generator: 'Generator') -> None: @@ -150,6 +153,8 @@ class Generator_f64(Generator_f32f64): # 2.4.1 Numeric Instructions self.promote_f32 = functools.partial(self.generator.add_statement, 'f64.promote_f32') + self.reinterpret_i64 = functools.partial(self.generator.add_statement, 'f64.reinterpret_i64') + self.reinterpret_i64 = functools.partial(self.generator.add_statement, 'f64.reinterpret_i64') class Generator_Local: def __init__(self, generator: 'Generator') -> None: diff --git a/tests/integration/test_typeclasses/test_reinterpretable.py b/tests/integration/test_typeclasses/test_reinterpretable.py new file mode 100644 index 0000000..627f99b --- /dev/null +++ b/tests/integration/test_typeclasses/test_reinterpretable.py @@ -0,0 +1,44 @@ +import pytest + +from phasm.type3.entry import Type3Exception + +from ..helpers import Suite + + +@pytest.mark.integration_test +def test_reinterpret_not_implemented(): + code_py = """ +class Foo: + val: i32 + +class Baz: + val: i32 + +@exported +def testEntry(x: Foo) -> Baz: + return reinterpret(x) +""" + + with pytest.raises(Type3Exception, match='Missing type class instantation: Reinterpretable Foo Baz'): + Suite(code_py).run_code() + +@pytest.mark.integration_test +@pytest.mark.parametrize('in_typ, in_val, out_typ, exp_val', [ + ('u32', 3225944128, 'f32', -3.1250152587890625, ), + ('u64', 13837591364432297984, 'f64', -3.1250152587890625, ), + ('i32', -1069023168, 'f32', -3.1250152587890625, ), + ('i64', -4609152709277253632, 'f64', -3.1250152587890625, ), + ('f32', -3.1250152587890625, 'u32', 3225944128, ), + ('f64', -3.1250152587890625, 'u64', 13837591364432297984, ), + ('f32', -3.1250152587890625, 'i32', -1069023168, ), + ('f64', -3.1250152587890625, 'i64', -4609152709277253632, ), +]) +def test_reinterpret_ok(in_typ, in_val, out_typ, exp_val): + code_py = f""" +@exported +def testEntry(x: {in_typ}) -> {out_typ}: + return reinterpret(x) +""" + + result = Suite(code_py).run_code(in_val) + assert exp_val == result.returned_value