phasm/phasm/stdlib/types.py
Johan B.W. de Vries 31d25683d9 Removes the special casing for foldl
Had to implement both functions as arguments and type
place holders (variables) for type constructors.

Had to implement functions as a type as well.

Still have to figure out how to pass functions around.
2025-05-18 19:55:36 +02:00

1609 lines
42 KiB
Python

"""
stdlib: Standard types that are not wasm primitives
"""
from phasm.stdlib import alloc
from phasm.type3.routers import TypeVariableLookup
from phasm.type3.types import IntType3, Type3
from phasm.wasmgenerator import Generator, VarType_Base, func_wrapper
from phasm.wasmgenerator import VarType_f32 as f32
from phasm.wasmgenerator import VarType_f64 as f64
from phasm.wasmgenerator import VarType_i32 as i32
from phasm.wasmgenerator import VarType_i64 as i64
@func_wrapper()
def __alloc_bytes__(g: Generator, length: i32) -> i32:
"""
Allocates room for a bytes instance, but does not write
anything to the allocated memory
"""
result = i32('result')
# Allocate the length of the byte string, as well
# as 4 bytes for a length header
g.local.get(length)
g.i32.const(4)
g.i32.add()
g.call(alloc.__alloc__)
# Store the address in a variable so we can use it up
# for writing the length header
g.local.tee(result)
g.local.get(length)
g.i32.store()
# Get the address back from the variable as return
g.local.get(result)
return i32('return') # To satisfy mypy
@func_wrapper()
def __subscript_bytes__(g: Generator, adr: i32, ofs: i32) -> i32:
"""
Returns an index from a bytes value
If ofs is more than the length of the bytes, this
function stop as unreachable.
adr i32 The pointer for the allocated bytes
ofs i32 The offset within the allocated bytes
"""
g.local.get(ofs)
g.local.get(adr)
g.i32.load()
g.i32.ge_u()
with g.if_():
# The offset is outside the allocated bytes
g.unreachable(comment='Out of bounds')
# The offset is less than the length
g.local.get(adr)
g.i32.const(4) # Bytes header
g.i32.add()
g.local.get(ofs)
g.i32.add()
g.i32.load8_u()
g.return_()
return i32('return') # To satisfy mypy
@func_wrapper()
def __u32_ord_min__(g: Generator, x: i32, y: i32) -> i32:
g.local.get(x)
g.local.get(y)
g.i32.lt_u()
with g.if_():
g.local.get(x)
g.return_()
g.local.get(y)
g.return_()
return i32('return') # To satisfy mypy
@func_wrapper()
def __u64_ord_min__(g: Generator, x: i64, y: i64) -> i64:
g.local.get(x)
g.local.get(y)
g.i64.lt_u()
with g.if_():
g.local.get(x)
g.return_()
g.local.get(y)
g.return_()
return i64('return') # To satisfy mypy
@func_wrapper()
def __i32_ord_min__(g: Generator, x: i32, y: i32) -> i32:
g.local.get(x)
g.local.get(y)
g.i32.lt_s()
with g.if_():
g.local.get(x)
g.return_()
g.local.get(y)
g.return_()
return i32('return') # To satisfy mypy
@func_wrapper()
def __i64_ord_min__(g: Generator, x: i64, y: i64) -> i64:
g.local.get(x)
g.local.get(y)
g.i64.lt_s()
with g.if_():
g.local.get(x)
g.return_()
g.local.get(y)
g.return_()
return i64('return') # To satisfy mypy
@func_wrapper()
def __u32_ord_max__(g: Generator, x: i32, y: i32) -> i32:
g.local.get(x)
g.local.get(y)
g.i32.gt_u()
with g.if_():
g.local.get(x)
g.return_()
g.local.get(y)
g.return_()
return i32('return') # To satisfy mypy
@func_wrapper()
def __u64_ord_max__(g: Generator, x: i64, y: i64) -> i64:
g.local.get(x)
g.local.get(y)
g.i64.gt_u()
with g.if_():
g.local.get(x)
g.return_()
g.local.get(y)
g.return_()
return i64('return') # To satisfy mypy
@func_wrapper()
def __i32_ord_max__(g: Generator, x: i32, y: i32) -> i32:
g.local.get(x)
g.local.get(y)
g.i32.gt_s()
with g.if_():
g.local.get(x)
g.return_()
g.local.get(y)
g.return_()
return i32('return') # To satisfy mypy
@func_wrapper()
def __i64_ord_max__(g: Generator, x: i64, y: i64) -> i64:
g.local.get(x)
g.local.get(y)
g.i64.gt_s()
with g.if_():
g.local.get(x)
g.return_()
g.local.get(y)
g.return_()
return i64('return') # To satisfy mypy
@func_wrapper()
def __i32_intnum_abs__(g: Generator, x: i32) -> i32:
# https://stackoverflow.com/a/14194764
y = i32('y')
# z = i32('z')
# y = x >> 31
g.local.get(x)
g.i32.const(31)
g.i32.shr_s() # Must be arithmetic shift
g.local.set(y)
# abs(x) = (x XOR y) - y
# (x XOR y)
g.local.get(x)
g.local.get(y)
g.i32.xor()
# - y
g.local.get(y)
g.i32.sub()
g.return_()
return i32('return') # To satisfy mypy
@func_wrapper()
def __i64_intnum_abs__(g: Generator, x: i64) -> i64:
# https://stackoverflow.com/a/14194764
y = i64('y')
# z = i64('z')
# y = x >> 31
g.local.get(x)
g.i64.const(31)
g.i64.shr_s() # Must be arithmetic shift
g.local.set(y)
# abs(x) = (x XOR y) - y
# (x XOR y)
g.local.get(x)
g.local.get(y)
g.i64.xor()
# - y
g.local.get(y)
g.i64.sub()
g.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
@func_wrapper()
def __u8_rotl__(g: Generator, x: i32, r: i32) -> i32:
s = i32('s') # The shifted part we need to overlay
# Handle cases where we need to shift more than 8 bits
g.local.get(r)
g.i32.const(8)
g.i32.rem_u()
g.local.set(r)
# Now do the rotation
g.local.get(x)
# 0000 0000 1100 0011
g.local.get(r)
# 0000 0000 1100 0011, 3
g.i32.shl()
# 0000 0110 0001 1000
g.local.tee(s)
# 0000 0110 0001 1000
g.i32.const(255)
# 0000 0110 0001 1000, 0000 0000 1111 1111
g.i32.and_()
# 0000 0000 0001 1000
g.local.get(s)
# 0000 0000 0001 1000, 0000 0110 0001 1000
g.i32.const(65280)
# 0000 0000 0001 1000, 0000 0110 0001 1000, 1111 1111 0000 0000
g.i32.and_()
# 0000 0000 0001 1000, 0000 0110 0000 0000
g.i32.const(8)
# 0000 0000 0001 1000, 0000 0110 0000 0000, 8
g.i32.shr_u()
# 0000 0000 0001 1000, 0000 0000 0000 0110
g.i32.or_()
# 0000 0000 0001 110
g.return_()
return i32('return') # To satisfy mypy
@func_wrapper()
def __u8_rotr__(g: Generator, x: i32, r: i32) -> i32:
s = i32('s') # The shifted part we need to overlay
# Handle cases where we need to shift more than 8 bits
g.local.get(r)
g.i32.const(8)
g.i32.rem_u()
g.local.set(r)
# Now do the rotation
g.local.get(x)
# 0000 0000 1100 0011
g.local.get(r)
# 0000 0000 1100 0011, 3
g.i32.rotr()
# 0110 0000 0000 0000 0000 0000 0001 1000
g.local.tee(s)
# 0110 0000 0000 0000 0000 0000 0001 1000
g.i32.const(255)
# 0110 0000 0000 0000 0000 0000 0001 1000, 0000 0000 1111 1111
g.i32.and_()
# 0000 0000 0000 0000 0000 0000 0001 1000
g.local.get(s)
# 0000 0000 0000 0000 0000 0000 0001 1000, 0110 0000 0000 0000 0000 0000 0001 1000
g.i32.const(4278190080)
# 0000 0000 0000 0000 0000 0000 0001 1000, 0110 0000 0000 0000 0000 0000 0001 1000, 1111 1111 0000 0000 0000 0000 0000 0000
g.i32.and_()
# 0000 0000 0000 0000 0000 0000 0001 1000, 0110 0000 0000 0000 0000 0000 0000 0000
g.i32.const(24)
# 0000 0000 0000 0000 0000 0000 0001 1000, 0110 0000 0000 0000 0000 0000 0000 0000, 24
g.i32.shr_u()
# 0000 0000 0000 0000 0000 0000 0001 1000, 0000 0000 0000 0000 0000 0000 0110 0000
g.i32.or_()
# 0000 0000 0000 0000 0000 0000 0111 1000
g.return_()
return i32('return') # To satisfy mypy
## ###
## class Eq
def u8_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.eq()
def u32_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.eq()
def u64_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.eq()
def i8_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.eq()
def i32_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.eq()
def i64_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.eq()
def f32_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.eq()
def f64_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.eq()
def u8_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ne()
def u32_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ne()
def u64_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.ne()
def i8_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ne()
def i32_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ne()
def i64_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.ne()
def f32_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.ne()
def f64_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.ne()
## ###
## class Ord
def u8_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_ord_min__')
def u32_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_ord_min__')
def u64_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u64_ord_min__')
def i8_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i32_ord_min__')
def i32_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i32_ord_min__')
def i64_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i64_ord_min__')
def f32_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.min()
def f64_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.min()
def u8_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_ord_max__')
def u32_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_ord_max__')
def u64_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u64_ord_max__')
def i8_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i32_ord_max__')
def i32_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i32_ord_max__')
def i64_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i64_ord_max__')
def f32_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.max()
def f64_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.max()
def u8_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.lt_u()
def u32_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.lt_u()
def u64_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.lt_u()
def i8_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.lt_s()
def i32_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.lt_s()
def i64_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.lt_s()
def f32_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.lt()
def f64_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.lt()
def u8_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.le_u()
def u32_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.le_u()
def u64_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.le_u()
def i8_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.le_s()
def i32_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.le_s()
def i64_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.le_s()
def f32_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.le()
def f64_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.le()
def u8_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.gt_u()
def u32_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.gt_u()
def u64_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.gt_u()
def i8_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.gt_s()
def i32_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.gt_s()
def i64_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.gt_s()
def f32_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.gt()
def f64_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.gt()
def u8_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ge_u()
def u32_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ge_u()
def u64_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.ge_u()
def i8_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ge_s()
def i32_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ge_s()
def i64_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.ge_s()
def f32_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.ge()
def f64_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.ge()
## ###
## class Bits
def u8_bits_logical_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shl()
g.i32.const(255)
g.i32.and_()
def u32_bits_logical_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shl()
def u64_bits_logical_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u()
g.i64.shl()
def u8_bits_logical_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shr_u()
def u32_bits_logical_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shr_u()
def u64_bits_logical_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u()
g.i64.shr_u()
def u8_bits_rotate_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u8_rotl__')
def u32_bits_rotate_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.rotl()
def u64_bits_rotate_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u()
g.i64.rotl()
def u8_bits_rotate_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u8_rotr__')
def u32_bits_rotate_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.rotr()
def u64_bits_rotate_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u()
g.i64.rotr()
def u8_bits_bitwise_and(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.and_()
def u32_bits_bitwise_and(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.and_()
def u64_bits_bitwise_and(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.and_()
def u8_bits_bitwise_or(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.or_()
def u32_bits_bitwise_or(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.or_()
def u64_bits_bitwise_or(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.or_()
def u8_bits_bitwise_xor(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.xor()
def u32_bits_bitwise_xor(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.xor()
def u64_bits_bitwise_xor(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.xor()
## ###
## class Fractional
def f32_fractional_ceil(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.ceil()
def f64_fractional_ceil(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.ceil()
def f32_fractional_floor(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.floor()
def f64_fractional_floor(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.floor()
def f32_fractional_trunc(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.trunc()
def f64_fractional_trunc(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.trunc()
def f32_fractional_nearest(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.nearest()
def f64_fractional_nearest(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.nearest()
def f32_fractional_div(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.div()
def f64_fractional_div(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.div()
## ###
## class Floating
def f32_floating_sqrt(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f32.sqrt')
def f64_floating_sqrt(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f64.sqrt')
## ###
## class Integral
def u32_integral_div(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.div_u')
def u64_integral_div(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.div_u')
def i32_integral_div(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.div_s')
def i64_integral_div(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.div_s')
def u32_integral_rem(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.rem_u')
def u64_integral_rem(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.rem_u')
def i32_integral_rem(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.rem_s')
def i64_integral_rem(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.rem_s')
## ###
## class NatNum
def u32_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.add')
def u64_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.add')
def i32_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.add')
def i64_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.add')
def f32_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f32.add')
def f64_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f64.add')
def u32_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.sub')
def u64_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.sub')
def i32_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.sub')
def i64_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.sub')
def f32_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f32.sub')
def f64_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f64.sub')
def u32_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.mul')
def u64_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.mul')
def i32_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.mul')
def i64_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.mul')
def f32_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f32.mul')
def f64_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f64.mul')
def u32_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shl()
def u64_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u()
g.i64.shl()
def i32_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shl()
def i64_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u()
g.i64.shl()
def f32_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_pow2__')
g.f32.convert_i32_u()
g.f32.mul()
def f64_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_pow2__')
g.f64.convert_i32_u()
g.f64.mul()
def u32_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shr_u()
def u64_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u()
g.i64.shr_u()
def i32_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shr_s()
def i64_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u()
g.i64.shr_s()
def f32_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_pow2__')
g.f32.convert_i32_u()
g.f32.div()
def f64_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_pow2__')
g.f64.convert_i32_u()
g.f64.div()
## ###
## class IntNum
def i32_intnum_abs(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i32_intnum_abs__')
def i64_intnum_abs(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i64_intnum_abs__')
def f32_intnum_abs(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.abs()
def f64_intnum_abs(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.abs()
def i32_intnum_neg(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.const(-1)
g.i32.mul()
def i64_intnum_neg(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.const(-1)
g.i64.mul()
def f32_intnum_neg(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.neg()
def f64_intnum_neg(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.neg()
## ###
## Class Sized
def dynamic_array_sized_len(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
# The length is stored in the first 4 bytes
g.i32.load()
def static_array_sized_len(g: Generator, tvl: TypeVariableLookup) -> None:
tv_map, tc_map = tvl
tvn_map = {
x.name: y
for x, y in tv_map.items()
}
sa_len = tvn_map['a*']
assert isinstance(sa_len, IntType3)
g.i32.const(sa_len.value)
## ###
## Extendable
def u8_u32_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
# No-op
# u8 is already stored as u32
pass
def u8_u64_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u()
def u32_u64_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u()
def i8_i32_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
# No-op
# i8 is already stored as i32
pass
def i8_i64_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_s()
def i32_i64_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_s()
def u8_u32_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.const(0xFF)
g.i32.and_()
def u8_u64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.wrap_i64()
g.i32.const(0xFF)
g.i32.and_()
def u32_u64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.wrap_i64()
def i8_i32_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.const(0xFF)
g.i32.and_()
def i8_i64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.wrap_i64()
def i32_i64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.wrap_i64()
## ###
## Promotable
def f32_f64_promote(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.promote_f32()
def f32_f64_demote(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.demote_f64()
## ###
## Foldable
def dynamic_array_sum(g: Generator, tvl: TypeVariableLookup) -> None:
raise NotImplementedError
def static_array_sum(g: Generator, tvl: TypeVariableLookup) -> None:
tv_map, tc_map = tvl
tvn_map = {
x.name: y
for x, y in tv_map.items()
}
sa_type = tvn_map['a']
sa_len = tvn_map['a*']
assert isinstance(sa_type, Type3)
assert isinstance(sa_len, IntType3)
if sa_len.value < 1:
raise NotImplementedError('Default value in case sum is empty')
# FIXME: We should probably use LOAD_STORE_TYPE_MAP for this?
mtyp_map = {
'u32': 'i32',
'u64': 'i64',
'i32': 'i32',
'i64': 'i64',
'f32': 'f32',
'f64': 'f64',
}
# FIXME: We should probably use calc_alloc_size for this?
type_var_size_map = {
'u32': 4,
'u64': 8,
'i32': 4,
'i64': 8,
'f32': 4,
'f64': 8,
}
type_var_add_generator = {
'u32': u32_natnum_add,
'u64': u64_natnum_add,
'i32': i32_natnum_add,
'i64': i64_natnum_add,
'f32': f32_natnum_add,
'f64': f64_natnum_add,
}
# By default, constructed types are passed as pointers
# FIXME: We don't know what add function to call
sa_type_mtyp = mtyp_map.get(sa_type.name, 'i32')
sa_type_alloc_size = type_var_size_map.get(sa_type.name, 4)
sa_type_add_gen = type_var_add_generator[sa_type.name]
# Definitions
sum_adr = g.temp_var(i32('sum_adr'))
sum_stop = g.temp_var(i32('sum_stop'))
# Stack before: [adr]
# Stack after: [sum]
# adr = {address of what's currently on stack}
# Stack: [adr] -> []
g.nop(comment=f'Start sum for {sa_type.name}[{sa_len.value}]')
g.local.set(sum_adr)
# stop = adr + ar_len * sa_type_alloc_size
# Stack: []
g.nop(comment='Calculate address at which to stop looping')
g.local.get(sum_adr)
g.i32.const(sa_len.value * sa_type_alloc_size)
g.i32.add()
g.local.set(sum_stop)
# sum = *adr
# Stack: [] -> [sum]
g.nop(comment='Get the first array value as starting point')
g.local.get(sum_adr)
g.add_statement(f'{sa_type_mtyp}.load')
# Since we did the first one, increase adr
# adr = adr + sa_type_alloc_size
# Stack: [sum] -> [sum]
g.local.get(sum_adr)
g.i32.const(sa_type_alloc_size)
g.i32.add()
g.local.set(sum_adr)
if sa_len.value > 1:
with g.loop(params=[sa_type_mtyp], result=sa_type_mtyp):
# sum = sum + *adr
# Stack: [sum] -> [sum + *adr]
g.nop(comment='Add array value')
g.local.get(sum_adr)
g.add_statement(f'{sa_type_mtyp}.load')
sa_type_add_gen(g, ({}, {}, ))
# adr = adr + sa_type_alloc_size
# Stack: [sum] -> [sum]
g.nop(comment='Calculate address of the next value')
g.local.get(sum_adr)
g.i32.const(sa_type_alloc_size)
g.i32.add()
g.local.tee(sum_adr)
# loop if adr < stop
g.nop(comment='Check if address exceeds array bounds')
g.local.get(sum_stop)
g.i32.lt_u()
g.br_if(0)
# else: sum x[1] === x => so we don't need to loop
g.nop(comment=f'Completed sum for {sa_type.name}[{sa_len.value}]')
# End result: [sum]
def dynamic_array_foldl(g: Generator, tvl: TypeVariableLookup) -> None:
tv_map, tc_map = tvl
tvn_map = {
x.name: y
for x, y in tv_map.items()
}
sa_type = tvn_map['a']
res_type = tvn_map['b']
assert isinstance(sa_type, Type3)
assert isinstance(res_type, Type3)
# FIXME: We should probably use LOAD_STORE_TYPE_MAP for this?
load_map = {
'u8': 'i32.load8_u',
'u32': 'i32.load',
'u64': 'i64.load',
'i8': 'i32.load8_s',
'i32': 'i32.load',
'i64': 'i64.load',
'f32': 'f32.load',
'f64': 'f64.load',
}
mtyp_map = {
'u32': 'i32',
'u64': 'i64',
'i32': 'i32',
'i64': 'i64',
'f32': 'f32',
'f64': 'f64',
}
mtyp_f_map: dict[str, type[VarType_Base]] = {
'i32': i32,
'i64': i64,
'f32': f32,
'f64': f64,
}
# FIXME: We should probably use calc_alloc_size for this?
type_var_size_map = {
'u8': 1,
'u32': 4,
'u64': 8,
'i8': 1,
'i32': 4,
'i64': 8,
'f32': 4,
'f64': 8,
}
# By default, constructed types are passed as pointers
# FIXME: We don't know what add function to call
sa_type_load = load_map.get(sa_type.name, 'i32')
sa_type_mtyp = mtyp_map.get(sa_type.name, 'i32')
sa_type_alloc_size = type_var_size_map.get(sa_type.name, 4)
res_type_mtyp = mtyp_map.get(res_type.name, 'i32')
res_type_mtyp_f = mtyp_f_map[res_type_mtyp]
# Definitions
fold_adr = g.temp_var(i32('fold_adr'))
fold_stop = g.temp_var(i32('fold_stop'))
fold_init = g.temp_var(res_type_mtyp_f('fold_init'))
fold_func = g.temp_var(i32('fold_func'))
fold_len = g.temp_var(i32('fold_len'))
with g.block(params=['i32', res_type_mtyp, 'i32'], result=res_type_mtyp, comment=f'foldl a={sa_type.name} b={res_type.name}'):
# Stack: [fn*, b, sa*] -> [fn*, b]
g.local.tee(fold_adr) # Store address, but also keep it for loading the length
g.i32.load() # Load the length
g.local.set(fold_len) # Store the length
# Stack: [fn*, b] -> [fn*]
g.local.set(fold_init)
# Stack: [fn*] -> []
g.local.set(fold_func)
# Stack: [] -> [b]
g.nop(comment='No applications if array is empty')
g.local.get(fold_init)
g.local.get(fold_len)
g.i32.eqz() # If the array is empty
g.br_if(0) # Then the base value is the result
# Stack: [b] -> [b] ; fold_adr=fold_adr + 4
g.nop(comment='Skip the header')
g.local.get(fold_adr)
g.i32.const(4)
g.i32.add()
g.local.set(fold_adr)
# Stack: [b] -> [b]
g.nop(comment='Apply the first function call')
g.local.get(fold_adr)
g.add_statement(sa_type_load)
g.local.get(fold_func)
g.add_statement(f'call_indirect (param {res_type_mtyp} {sa_type_mtyp}) (result {res_type_mtyp})')
# Stack: [b] -> [b]
g.nop(comment='No loop if there is only one item')
g.local.get(fold_len)
g.i32.const(1)
g.i32.eq()
g.br_if(0) # just one value, don't need to loop
# Stack: [b] -> [b] ; fold_stop=fold_adr + (sa_len.value * sa_type_alloc_size)
g.nop(comment='Calculate address at which to stop looping')
g.local.get(fold_adr)
g.local.get(fold_len)
g.i32.const(sa_type_alloc_size)
g.i32.mul()
g.i32.add()
g.local.set(fold_stop)
# Stack: [b] -> [b] ; fold_adr = fold_adr + sa_type_alloc_size
g.nop(comment='Calculate address of the next value')
g.local.get(fold_adr)
g.i32.const(sa_type_alloc_size)
g.i32.add()
g.local.set(fold_adr)
with g.loop(params=[res_type_mtyp], result=res_type_mtyp):
# Stack: [b] -> [b]
g.nop(comment='Apply function call')
g.local.get(fold_adr)
g.add_statement(sa_type_load)
g.local.get(fold_func)
g.add_statement(f'call_indirect (param {res_type_mtyp} {sa_type_mtyp}) (result {res_type_mtyp})')
# Stack: [b] -> [b] ; fold_adr = fold_adr + sa_type_alloc_size
g.nop(comment='Calculate address of the next value')
g.local.get(fold_adr)
g.i32.const(sa_type_alloc_size)
g.i32.add()
g.local.tee(fold_adr)
# loop if adr > stop
# Stack: [b] -> [b]
g.nop(comment='Check if address exceeds array bounds')
g.local.get(fold_stop)
g.i32.lt_u()
g.br_if(0)
# Stack: [b]
def static_array_foldl(g: Generator, tvl: TypeVariableLookup) -> None:
tv_map, tc_map = tvl
tvn_map = {
x.name: y
for x, y in tv_map.items()
}
sa_type = tvn_map['a']
sa_len = tvn_map['a*']
res_type = tvn_map['b']
assert isinstance(sa_type, Type3)
assert isinstance(sa_len, IntType3)
assert isinstance(res_type, Type3)
# FIXME: We should probably use LOAD_STORE_TYPE_MAP for this?
mtyp_map = {
'u32': 'i32',
'u64': 'i64',
'i32': 'i32',
'i64': 'i64',
'f32': 'f32',
'f64': 'f64',
}
mtyp_f_map: dict[str, type[VarType_Base]] = {
'i32': i32,
'i64': i64,
'f32': f32,
'f64': f64,
}
# FIXME: We should probably use calc_alloc_size for this?
type_var_size_map = {
'u32': 4,
'u64': 8,
'i32': 4,
'i64': 8,
'f32': 4,
'f64': 8,
}
# By default, constructed types are passed as pointers
# FIXME: We don't know what add function to call
sa_type_mtyp = mtyp_map.get(sa_type.name, 'i32')
sa_type_alloc_size = type_var_size_map.get(sa_type.name, 4)
res_type_mtyp = mtyp_map.get(res_type.name, 'i32')
res_type_mtyp_f = mtyp_f_map[res_type_mtyp]
# Definitions
fold_adr = g.temp_var(i32('fold_adr'))
fold_stop = g.temp_var(i32('fold_stop'))
fold_init = g.temp_var(res_type_mtyp_f('fold_init'))
fold_func = g.temp_var(i32('fold_func'))
with g.block(params=['i32', res_type_mtyp, 'i32'], result=res_type_mtyp, comment=f'foldl a={sa_type.name} a*={sa_len.value} b={res_type.name}'):
# Stack: [fn*, b, sa*] -> [fn*, b]
g.local.set(fold_adr)
# Stack: [fn*, b] -> [fn*]
g.local.set(fold_init)
# Stack: [fn*] -> []
g.local.set(fold_func)
if sa_len.value < 1:
g.local.get(fold_init)
return
# Stack: [] -> [b]
g.nop(comment='Apply the first function call')
g.local.get(fold_init)
g.local.get(fold_adr)
g.add_statement(f'{sa_type_mtyp}.load')
g.local.get(fold_func)
g.add_statement(f'call_indirect (param {res_type_mtyp} {sa_type_mtyp}) (result {res_type_mtyp})')
if sa_len.value > 1:
# Stack: [b] -> [b] ; fold_stop=fold_adr + (sa_len.value * sa_type_alloc_size)
g.nop(comment='Calculate address at which to stop looping')
g.local.get(fold_adr)
g.i32.const(sa_len.value * sa_type_alloc_size)
g.i32.add()
g.local.set(fold_stop)
# Stack: [b] -> [b] ; fold_adr = fold_adr + sa_type_alloc_size
g.nop(comment='Calculate address of the next value')
g.local.get(fold_adr)
g.i32.const(sa_type_alloc_size)
g.i32.add()
g.local.set(fold_adr)
with g.loop(params=[res_type_mtyp], result=res_type_mtyp):
# Stack: [b] -> [b]
g.nop(comment='Apply function call')
g.local.get(fold_adr)
g.add_statement(f'{sa_type_mtyp}.load')
g.local.get(fold_func)
g.add_statement(f'call_indirect (param {res_type_mtyp} {sa_type_mtyp}) (result {res_type_mtyp})')
# Stack: [b] -> [b] ; fold_adr = fold_adr + sa_type_alloc_size
g.nop(comment='Calculate address of the next value')
g.local.get(fold_adr)
g.i32.const(sa_type_alloc_size)
g.i32.add()
g.local.tee(fold_adr)
# loop if adr > stop
# Stack: [b] -> [b]
g.nop(comment='Check if address exceeds array bounds')
g.local.get(fold_stop)
g.i32.lt_u()
g.br_if(0)
# else: just one value, don't need to loop
# Stack: [b]
def dynamic_array_foldr(g: Generator, tvl: TypeVariableLookup) -> None:
raise NotImplementedError
def static_array_foldr(g: Generator, tvl: TypeVariableLookup) -> None:
tv_map, tc_map = tvl
tvn_map = {
x.name: y
for x, y in tv_map.items()
}
sa_type = tvn_map['a']
sa_len = tvn_map['a*']
res_type = tvn_map['b']
assert isinstance(sa_type, Type3)
assert isinstance(sa_len, IntType3)
assert isinstance(res_type, Type3)
# FIXME: We should probably use LOAD_STORE_TYPE_MAP for this?
mtyp_map = {
'u32': 'i32',
'u64': 'i64',
'i32': 'i32',
'i64': 'i64',
'f32': 'f32',
'f64': 'f64',
}
mtyp_f_map: dict[str, type[VarType_Base]] = {
'i32': i32,
'i64': i64,
'f32': f32,
'f64': f64,
}
# FIXME: We should probably use calc_alloc_size for this?
type_var_size_map = {
'u32': 4,
'u64': 8,
'i32': 4,
'i64': 8,
'f32': 4,
'f64': 8,
}
# By default, constructed types are passed as pointers
sa_type_mtyp = mtyp_map.get(sa_type.name, 'i32')
sa_type_alloc_size = type_var_size_map.get(sa_type.name, 4)
res_type_mtyp = mtyp_map.get(res_type.name, 'i32')
res_type_mtyp_f = mtyp_f_map[res_type_mtyp]
# Definitions
fold_adr = g.temp_var(i32('fold_adr'))
fold_stop = g.temp_var(i32('fold_stop'))
fold_tmp = g.temp_var(res_type_mtyp_f('fold_tmp'))
fold_func = g.temp_var(i32('fold_func'))
with g.block(params=['i32', res_type_mtyp, 'i32'], result=res_type_mtyp, comment=f'foldr a={sa_type.name} a*={sa_len.value} b={res_type.name}'):
# Stack: [fn*, b, sa*] -> [fn*, b] ; fold_adr=fn*, fold_tmp=b, fold_func=fn*
g.local.set(fold_adr)
# Stack: [fn*, b] -> [fn*]
g.local.set(fold_tmp)
# Stack: [fn*] -> []
g.local.set(fold_func)
if sa_len.value < 1:
g.local.get(fold_tmp)
return
# Stack: [] -> [] ; fold_stop=fold_adr
g.nop(comment='Calculate address at which to stop looping')
g.local.get(fold_adr)
g.local.set(fold_stop)
# Stack: [] -> [] ; fold_adr=fold_adr + (sa_len.value - 1) * sa_type_alloc_size
g.nop(comment='Calculate address at which to stop looping')
g.local.get(fold_adr)
g.i32.const((sa_len.value - 1) * sa_type_alloc_size)
g.i32.add()
g.local.set(fold_adr)
# Stack: [] -> [b]
g.nop(comment='Get the init value and first array value as starting point')
g.local.get(fold_adr)
g.add_statement(f'{sa_type_mtyp}.load')
g.local.get(fold_tmp)
g.local.get(fold_func)
g.add_statement(f'call_indirect (param {sa_type_mtyp} {res_type_mtyp}) (result {res_type_mtyp})')
if sa_len.value > 1:
# Stack: [b] -> [b] ; fold_adr = fold_adr - sa_type_alloc_size
g.nop(comment='Calculate address of the next value')
g.local.get(fold_adr)
g.i32.const(sa_type_alloc_size)
g.i32.sub()
g.local.set(fold_adr)
with g.loop(params=[res_type_mtyp], result=res_type_mtyp):
g.nop(comment='Apply function call')
# Stack [b] since we don't have proper stack switching opcodes
# Stack: [b] -> []
g.local.set(fold_tmp)
# Stack: [] -> [a]
g.local.get(fold_adr)
g.add_statement(f'{sa_type_mtyp}.load')
# Stack [a] -> [a, b]
g.local.get(fold_tmp)
# Stack [a, b] -> [b]
g.local.get(fold_func)
g.add_statement(f'call_indirect (param {sa_type_mtyp} {res_type_mtyp}) (result {res_type_mtyp})')
# Stack: [b] -> [b] ; fold_adr = fold_adr - sa_type_alloc_size
g.nop(comment='Calculate address of the next value')
g.local.get(fold_adr)
g.i32.const(sa_type_alloc_size)
g.i32.sub()
g.local.tee(fold_adr)
# loop if adr >= stop
# Stack: [b] -> [b]
g.nop(comment='Check if address exceeds array bounds')
g.local.get(fold_stop)
g.i32.ge_u()
g.br_if(0)
# else: just one value, don't need to loop
# Stack: [b]