Implements u16 / i16 support

Keep in mind that WebAssembly is u32 native by default,
some operations may be more expensive than you expect
them to be.
This commit is contained in:
Johan B.W. de Vries 2025-05-25 15:31:23 +02:00
parent d017ebe096
commit 84e7c42ea4
10 changed files with 414 additions and 15 deletions

View File

@ -208,6 +208,12 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Expression)
wgn.i32.const(inp.value)
return
if inp.type3 in (prelude.i16, prelude.u16, ):
# No native u16 type - treat as i32, with caution
assert isinstance(inp.value, int)
wgn.i32.const(inp.value)
return
if inp.type3 in (prelude.i32, prelude.u32, ):
assert isinstance(inp.value, int)
wgn.i32.const(inp.value)
@ -476,6 +482,12 @@ def module_data_u8(inp: int) -> bytes:
"""
return struct.pack('<B', inp)
def module_data_u16(inp: int) -> bytes:
"""
Compile: module data, u16 value
"""
return struct.pack('<H', inp)
def module_data_u32(inp: int) -> bytes:
"""
Compile: module data, u32 value
@ -494,6 +506,12 @@ def module_data_i8(inp: int) -> bytes:
"""
return struct.pack('<b', inp)
def module_data_i16(inp: int) -> bytes:
"""
Compile: module data, i16 value
"""
return struct.pack('<h', inp)
def module_data_i32(inp: int) -> bytes:
"""
Compile: module data, i32 value
@ -549,6 +567,12 @@ def module_data(inp: ourlang.ModuleData) -> bytes:
data_list.append(module_data_u8(constant.value))
continue
if constant.type3 == prelude.u16:
assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, int)
data_list.append(module_data_u16(constant.value))
continue
if constant.type3 == prelude.u32:
assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, int)
@ -567,6 +591,12 @@ def module_data(inp: ourlang.ModuleData) -> bytes:
data_list.append(module_data_i8(constant.value))
continue
if constant.type3 == prelude.i16:
assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, int)
data_list.append(module_data_i16(constant.value))
continue
if constant.type3 == prelude.i32:
assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, int)
@ -653,6 +683,8 @@ def module(inp: ourlang.Module) -> wasm.Module:
stdlib_types.__u32_pow2__,
stdlib_types.__u8_rotl__,
stdlib_types.__u8_rotr__,
stdlib_types.__u16_rotl__,
stdlib_types.__u16_rotr__,
] + [
function(inp, x)
for x in inp.functions.values()

View File

@ -110,6 +110,13 @@ The unsigned 8-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^8.
"""
u16 = Type3('u16', TypeApplication_Nullary(None, None))
"""
The unsigned 16-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^16.
"""
u32 = Type3('u32', TypeApplication_Nullary(None, None))
"""
The unsigned 32-bit integer type.
@ -132,6 +139,14 @@ Operations on variables employ modular arithmetic, with modulus 2^8, but
with the middel point being 0.
"""
i16 = Type3('i16', TypeApplication_Nullary(None, None))
"""
The signed 16-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^16, but
with the middel point being 0.
"""
i32 = Type3('i32', TypeApplication_Nullary(None, None))
"""
The unsigned 32-bit integer type.
@ -210,6 +225,10 @@ instance_type_class(Eq, u8, operators={
'==': stdtypes.u8_eq_equals,
'!=': stdtypes.u8_eq_not_equals,
})
instance_type_class(Eq, u16, operators={
'==': stdtypes.u16_eq_equals,
'!=': stdtypes.u16_eq_not_equals,
})
instance_type_class(Eq, u32, operators={
'==': stdtypes.u32_eq_equals,
'!=': stdtypes.u32_eq_not_equals,
@ -222,6 +241,10 @@ instance_type_class(Eq, i8, operators={
'==': stdtypes.i8_eq_equals,
'!=': stdtypes.i8_eq_not_equals,
})
instance_type_class(Eq, i16, operators={
'==': stdtypes.i16_eq_equals,
'!=': stdtypes.i16_eq_not_equals,
})
instance_type_class(Eq, i32, operators={
'==': stdtypes.i32_eq_equals,
'!=': stdtypes.i32_eq_not_equals,
@ -258,6 +281,15 @@ instance_type_class(Ord, u8, methods={
'>': stdtypes.u8_ord_greater_than,
'>=': stdtypes.u8_ord_greater_than_or_equal,
})
instance_type_class(Ord, u16, methods={
'min': stdtypes.u16_ord_min,
'max': stdtypes.u16_ord_max,
}, operators={
'<': stdtypes.u16_ord_less_than,
'<=': stdtypes.u16_ord_less_than_or_equal,
'>': stdtypes.u16_ord_greater_than,
'>=': stdtypes.u16_ord_greater_than_or_equal,
})
instance_type_class(Ord, u32, methods={
'min': stdtypes.u32_ord_min,
'max': stdtypes.u32_ord_max,
@ -285,6 +317,15 @@ instance_type_class(Ord, i8, methods={
'>': stdtypes.i8_ord_greater_than,
'>=': stdtypes.i8_ord_greater_than_or_equal,
})
instance_type_class(Ord, i16, methods={
'min': stdtypes.i16_ord_min,
'max': stdtypes.i16_ord_max,
}, operators={
'<': stdtypes.i16_ord_less_than,
'<=': stdtypes.i16_ord_less_than_or_equal,
'>': stdtypes.i16_ord_greater_than,
'>=': stdtypes.i16_ord_greater_than_or_equal,
})
instance_type_class(Ord, i32, methods={
'min': stdtypes.i32_ord_min,
'max': stdtypes.i32_ord_max,
@ -344,6 +385,16 @@ instance_type_class(Bits, u8, methods={
'|': stdtypes.u8_bits_bitwise_or,
'^': stdtypes.u8_bits_bitwise_xor,
})
instance_type_class(Bits, u16, methods={
'shl': stdtypes.u16_bits_logical_shift_left,
'shr': stdtypes.u16_bits_logical_shift_right,
'rotl': stdtypes.u16_bits_rotate_left,
'rotr': stdtypes.u16_bits_rotate_right,
}, operators={
'&': stdtypes.u16_bits_bitwise_and,
'|': stdtypes.u16_bits_bitwise_or,
'^': stdtypes.u16_bits_bitwise_xor,
})
instance_type_class(Bits, u32, methods={
'shl': stdtypes.u32_bits_logical_shift_left,
'shr': stdtypes.u32_bits_logical_shift_right,
@ -516,6 +567,10 @@ Extendable = Type3Class('Extendable', (a, b, ), methods={
'wrap': [b, a],
}, operators={})
instance_type_class(Extendable, u8, u16, methods={
'extend': stdtypes.u8_u16_extend,
'wrap': stdtypes.u8_u16_wrap,
})
instance_type_class(Extendable, u8, u32, methods={
'extend': stdtypes.u8_u32_extend,
'wrap': stdtypes.u8_u32_wrap,
@ -524,10 +579,22 @@ instance_type_class(Extendable, u8, u64, methods={
'extend': stdtypes.u8_u64_extend,
'wrap': stdtypes.u8_u64_wrap,
})
instance_type_class(Extendable, u16, u32, methods={
'extend': stdtypes.u16_u32_extend,
'wrap': stdtypes.u16_u32_wrap,
})
instance_type_class(Extendable, u16, u64, methods={
'extend': stdtypes.u16_u64_extend,
'wrap': stdtypes.u16_u64_wrap,
})
instance_type_class(Extendable, u32, u64, methods={
'extend': stdtypes.u32_u64_extend,
'wrap': stdtypes.u32_u64_wrap,
})
instance_type_class(Extendable, i8, i16, methods={
'extend': stdtypes.i8_i16_extend,
'wrap': stdtypes.i8_i16_wrap,
})
instance_type_class(Extendable, i8, i32, methods={
'extend': stdtypes.i8_i32_extend,
'wrap': stdtypes.i8_i32_wrap,
@ -536,6 +603,14 @@ instance_type_class(Extendable, i8, i64, methods={
'extend': stdtypes.i8_i64_extend,
'wrap': stdtypes.i8_i64_wrap,
})
instance_type_class(Extendable, i16, i32, methods={
'extend': stdtypes.i16_i32_extend,
'wrap': stdtypes.i16_i32_wrap,
})
instance_type_class(Extendable, i16, i64, methods={
'extend': stdtypes.i16_i64_extend,
'wrap': stdtypes.i16_i64_wrap,
})
instance_type_class(Extendable, i32, i64, methods={
'extend': stdtypes.i32_i64_extend,
'wrap': stdtypes.i32_i64_wrap,
@ -644,9 +719,11 @@ PRELUDE_TYPES: dict[str, Type3] = {
'none': none,
'bool': bool_,
'u8': u8,
'u16': u16,
'u32': u32,
'u64': u64,
'i8': i8,
'i16': i16,
'i32': i32,
'i64': i64,
'f32': f32,

View File

@ -40,6 +40,8 @@ TYPE_INFO_MAP: Mapping[str, TypeInfo] = {
'u8': TypeInfo('u8', WasmTypeInt32, 'i32.load8_u', 'i32.store8', 1),
'i8': TypeInfo('i8', WasmTypeInt32, 'i32.load8_s', 'i32.store8', 1),
'u16': TypeInfo('u16', WasmTypeInt32, 'i32.load16_u', 'i32.store16', 2),
'i16': TypeInfo('i16', WasmTypeInt32, 'i32.load16_s', 'i32.store16', 2),
'u32': TypeInfo('u32', WasmTypeInt32, 'i32.load', 'i32.store', 4),
'u64': TypeInfo('u64', WasmTypeInt64, 'i64.load', 'i64.store', 8),
@ -434,6 +436,86 @@ def __u8_rotr__(g: Generator, x: i32, r: i32) -> i32:
return i32('return') # To satisfy mypy
@func_wrapper()
def __u16_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)
# 0x0000B2C3
g.local.get(r)
# 0x0000B2C3, 3
g.i32.shl()
# 0x00059618
g.local.tee(s)
# 0x00059618
g.i32.const(0xFFFF)
# 0x00059618, 0x0000FFFF
g.i32.and_()
# 0x00009618
g.local.get(s)
# 0x00009618, 0x00059618
g.i32.const(0xFFFF0000)
# 0x00009618, 0x00059618, 0xFFFF0000
g.i32.and_()
# 0x00009618, 0x00050000
g.i32.const(16)
# 0x00009618, 0x00050000, 16
g.i32.shr_u()
# 0x00009618, 0x00000005, 16
g.i32.or_()
# 0x0000961D
g.return_()
return i32('return') # To satisfy mypy
@func_wrapper()
def __u16_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 16 bits
g.local.get(r)
g.i32.const(16)
g.i32.rem_u()
g.local.set(r)
# Now do the rotation
g.local.get(x)
# 0x0000B2C3
g.local.get(r)
# 0x0000B2C3, 3
g.i32.rotr()
# 0x60001658
g.local.tee(s)
# 0x60001658
g.i32.const(0xFFFF)
# 0x60001658, 0x0000FFFF
g.i32.and_()
# 0x00001658
g.local.get(s)
# 0x00001658, 0x60001658
g.i32.const(0xFFFF0000)
# 0x00001658, 0x60001658, 0xFFFF0000
g.i32.and_()
# 0x00001658, 0x60000000
g.i32.const(16)
# 0x00001658, 0x60000000, 16
g.i32.shr_u()
# 0x00001658, 0x00006000
g.i32.or_()
# 0x00007658
g.return_()
return i32('return') # To satisfy mypy
## ###
## class Eq
@ -441,6 +523,10 @@ def u8_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.eq()
def u16_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()
@ -453,6 +539,10 @@ def i8_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.eq()
def i16_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()
@ -473,6 +563,10 @@ def u8_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ne()
def u16_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()
@ -485,6 +579,10 @@ def i8_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ne()
def i16_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()
@ -508,6 +606,10 @@ def u8_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_ord_min__')
def u16_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__')
@ -520,6 +622,10 @@ def i8_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i32_ord_min__')
def i16_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__')
@ -540,6 +646,10 @@ def u8_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_ord_max__')
def u16_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__')
@ -552,6 +662,10 @@ def i8_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i32_ord_max__')
def i16_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__')
@ -573,6 +687,10 @@ def u8_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.lt_u()
def u16_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()
@ -585,6 +703,10 @@ def i8_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.lt_s()
def i16_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()
@ -605,6 +727,10 @@ def u8_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.le_u()
def u16_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()
@ -617,6 +743,10 @@ def i8_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.le_s()
def i16_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()
@ -637,6 +767,10 @@ def u8_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.gt_u()
def u16_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()
@ -649,6 +783,10 @@ def i8_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.gt_s()
def i16_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()
@ -669,6 +807,10 @@ def u8_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> No
del tv_map
g.i32.ge_u()
def u16_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()
@ -681,6 +823,10 @@ def i8_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> No
del tv_map
g.i32.ge_s()
def i16_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()
@ -703,7 +849,13 @@ def f64_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> N
def u8_bits_logical_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shl()
g.i32.const(255)
g.i32.const(0xFF)
g.i32.and_()
def u16_bits_logical_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shl()
g.i32.const(0xFFFF)
g.i32.and_()
def u32_bits_logical_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
@ -719,6 +871,10 @@ def u8_bits_logical_shift_right(g: Generator, tv_map: TypeVariableLookup) -> Non
del tv_map
g.i32.shr_u()
def u16_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()
@ -732,6 +888,10 @@ def u8_bits_rotate_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u8_rotl__')
def u16_bits_rotate_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u16_rotl__')
def u32_bits_rotate_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.rotl()
@ -745,6 +905,10 @@ def u8_bits_rotate_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u8_rotr__')
def u16_bits_rotate_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u16_rotr__')
def u32_bits_rotate_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.rotr()
@ -758,6 +922,10 @@ def u8_bits_bitwise_and(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.and_()
def u16_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_()
@ -770,6 +938,10 @@ def u8_bits_bitwise_or(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.or_()
def u16_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_()
@ -782,6 +954,10 @@ def u8_bits_bitwise_xor(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.xor()
def u16_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()
@ -1074,6 +1250,12 @@ def static_array_sized_len(g: Generator, tvl: TypeVariableLookup) -> None:
## ###
## Extendable
def u8_u16_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
# No-op
# u8 and u16 are both stored as u32
pass
def u8_u32_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
# No-op
@ -1084,10 +1266,26 @@ def u8_u64_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u()
def u16_u32_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
# No-op
# u16 is already stored as u32
pass
def u16_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_i16_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
# No-op
# i8 is already stored as i32
pass
def i8_i32_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
# No-op
@ -1098,10 +1296,25 @@ def i8_i64_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_s()
def i16_i32_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
# No-op
# i16 is already stored as i32
pass
def i16_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_u16_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.const(0xFF)
g.i32.and_()
def u8_u32_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.const(0xFF)
@ -1113,10 +1326,26 @@ def u8_u64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
g.i32.const(0xFF)
g.i32.and_()
def u16_u32_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.const(0xFFFF)
g.i32.and_()
def u16_u64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.wrap_i64()
g.i32.const(0xFFFF)
g.i32.and_()
def u32_u64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.wrap_i64()
def i8_i16_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.const(0xFF)
g.i32.and_()
def i8_i32_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.const(0xFF)
@ -1125,6 +1354,19 @@ def i8_i32_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
def i8_i64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.wrap_i64()
g.i32.const(0xFF)
g.i32.and_()
def i16_i32_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.const(0xFFFF)
g.i32.and_()
def i16_i64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.wrap_i64()
g.i32.const(0xFFFF)
g.i32.and_()
def i32_i64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map

View File

@ -538,9 +538,11 @@ class LiteralFitsConstraint(ConstraintBase):
def check(self) -> CheckResult:
int_table: Dict[str, Tuple[int, bool]] = {
'u8': (1, False),
'u16': (2, False),
'u32': (4, False),
'u64': (8, False),
'i8': (1, True),
'i16': (2, True),
'i32': (4, True),
'i64': (8, True),
}

View File

@ -124,12 +124,12 @@ class Suite:
runner.interpreter_dump_memory(sys.stderr)
for arg, arg_typ in zip(args, func_args, strict=True):
if arg_typ in (prelude.u8, prelude.u32, prelude.u64, ):
if arg_typ in (prelude.u8, prelude.u16, prelude.u32, prelude.u64, ):
assert isinstance(arg, int)
wasm_args.append(arg)
continue
if arg_typ in (prelude.i8, prelude.i32, prelude.i64, ):
if arg_typ in (prelude.i8, prelude.i16, prelude.i32, prelude.i64, ):
assert isinstance(arg, int)
wasm_args.append(arg)
continue
@ -169,9 +169,11 @@ def write_header(textio: TextIO, msg: str) -> None:
WRITE_LOOKUP_MAP = {
'u8': compiler.module_data_u8,
'u16': compiler.module_data_u16,
'u32': compiler.module_data_u32,
'u64': compiler.module_data_u64,
'i8': compiler.module_data_i8,
'i16': compiler.module_data_i16,
'i32': compiler.module_data_i32,
'i64': compiler.module_data_i64,
'f32': compiler.module_data_f32,
@ -302,7 +304,7 @@ def _load_memory_stored_returned_value(
assert isinstance(wasm_value, int), wasm_value
return 0 != wasm_value
if ret_type3 in (prelude.i8, prelude.i32, prelude.i64):
if ret_type3 in (prelude.i8, prelude.i16, prelude.i32, prelude.i64):
assert isinstance(wasm_value, int), wasm_value
if ret_type3 is prelude.i8:
@ -311,9 +313,15 @@ def _load_memory_stored_returned_value(
data = struct.pack('<i', wasm_value)
wasm_value, = struct.unpack('<bxxx', data)
if ret_type3 is prelude.i16:
# Values are actually i32
# Have to reinterpret to load proper value
data = struct.pack('<i', wasm_value)
wasm_value, = struct.unpack('<hxx', data)
return wasm_value
if ret_type3 in (prelude.u8, prelude.u32, prelude.u64):
if ret_type3 in (prelude.u8, prelude.u16, prelude.u32, prelude.u64):
assert isinstance(wasm_value, int), wasm_value
if wasm_value < 0:
@ -346,6 +354,9 @@ def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> An
if typ is prelude.u8:
return struct.unpack('<B', inp)[0]
if typ is prelude.u16:
return struct.unpack('<H', inp)[0]
if typ is prelude.u32:
return struct.unpack('<I', inp)[0]
@ -355,6 +366,9 @@ def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> An
if typ is prelude.i8:
return struct.unpack('<b', inp)[0]
if typ is prelude.i16:
return struct.unpack('<h', inp)[0]
if typ is prelude.i32:
return struct.unpack('<i', inp)[0]

View File

@ -1,14 +1,17 @@
{
"TYPE_NAME": "struct_all_primitives",
"TYPE": "StructallPrimitives",
"VAL0": "StructallPrimitives(1, 4, 8, 1, -1, 4, -4, 8, -8, 125.125, -125.125, 5000.5, -5000.5, b'Hello, world!')",
"VAL0": "StructallPrimitives(1, 2, 4, 8, 1, -1, 2, -2, 4, -4, 8, -8, 125.125, -125.125, 5000.5, -5000.5, b'Hello, world!')",
"CODE_HEADER": [
"class StructallPrimitives:",
" val00: u8",
" val03: u16",
" val01: u32",
" val02: u64",
" val10: i8",
" val11: i8",
" val16: i16",
" val17: i16",
" val12: i32",
" val13: i32",
" val14: i64",
@ -22,10 +25,13 @@
"PYTHON": {
"VAL0": {
"val00": 1,
"val03": 2,
"val01": 4,
"val02": 8,
"val10": 1,
"val11": -1,
"val16": 2,
"val17": -2,
"val12": 4,
"val13": -4,
"val14": 8,

View File

@ -18,7 +18,7 @@ class ExpResult:
) + ')'
TYPE_LIST = [
'u8', 'u32', 'u64',
'u8', 'u16', 'u32', 'u64',
]
@pytest.mark.integration_test

View File

@ -4,7 +4,7 @@ from phasm.type3.entry import Type3Exception
from ..helpers import Suite
INT_TYPES = ['u8', 'u32', 'u64', 'i8', 'i32', 'i64']
INT_TYPES = ['u8', 'u16', 'u32', 'u64', 'i8', 'i16', 'i32', 'i64']
FLOAT_TYPES = ['f32', 'f64']
TYPE_MAP = {

View File

@ -5,12 +5,18 @@ from phasm.type3.entry import Type3Exception
from ..helpers import Suite
EXTENTABLE = [
('u8', 'u16', ),
('u8', 'u32', ),
('u8', 'u64', ),
('u16', 'u32', ),
('u16', 'u64', ),
('u32', 'u64', ),
('i8', 'i16', ),
('i8', 'i32', ),
('i8', 'i64', ),
('i16', 'i32', ),
('i16', 'i64', ),
('i32', 'i64', ),
]
@ -48,17 +54,26 @@ def testEntry() -> {ext_to}:
@pytest.mark.integration_test
@pytest.mark.parametrize('ext_from,in_put,ext_to,exp_out', [
('u8', 241, 'u16', 241),
('u8', 241, 'u32', 241),
('u32', 4059165169, 'u64', 4059165169),
('u8', 241, 'u64', 241),
('u16', 7681, 'u32', 7681),
('u16', 7681, 'u64', 7681),
('u32', 4059165169, 'u64', 4059165169),
('i8', 113, 'i16', 113),
('i8', 113, 'i32', 113),
('i32', 1911681521, 'i64', 1911681521),
('i8', 113, 'i64', 113),
('i8', 3585, 'i32', 3585),
('i8', 3585, 'i64', 3585),
('i32', 1911681521, 'i64', 1911681521),
('i8', -15, 'i16', -15),
('i8', -15, 'i32', -15),
('i32', -15, 'i64', -15),
('i8', -15, 'i64', -15),
('i16', -15, 'i32', -15),
('i16', -15, 'i64', -15),
('i32', -15, 'i64', -15),
])
def test_extend_results(ext_from, ext_to, in_put, exp_out):
@ -89,14 +104,23 @@ def testEntry() -> {ext_from}:
@pytest.mark.integration_test
@pytest.mark.parametrize('ext_to,in_put,ext_from,exp_out', [
('u16', 0xF1F1, 'u8', 0xF1),
('u32', 0xF1F1F1F1, 'u16', 0xF1F1),
('u32', 0xF1F1F1F1, 'u8', 0xF1),
('u64', 0xF1F1F1F1F1F1F1F1, 'u32', 0xF1F1F1F1),
('u64', 0xF1F1F1F1F1F1F1F1, 'u16', 0xF1F1),
('u64', 0xF1F1F1F1F1F1F1F1, 'u8', 0xF1),
('i16', 0xF171, 'i8', 113),
('i16', 0xF1F1, 'i8', -15),
('i32', 0xF1F171F1, 'i16', 29169),
('i32', 0xF1F1F1F1, 'i16', -3599),
('i32', 0xF1F1F171, 'i8', 113),
('i32', 0xF1F1F1F1, 'i8', -15),
('i64', 0x71F1F1F171F1F1F1, 'i32', 1911681521),
('i64', 0x71F1F1F1F1F1F1F1, 'i32', -235802127),
('i64', 0xF1F1F1F171F1F1F1, 'i32', 1911681521),
('i64', 0xF1F1F1F1F1F1F1F1, 'i32', -235802127),
('i64', 0xF1F1F1F1F1F171F1, 'i16', 29169),
('i64', 0xF1F1F1F1F1F1F1F1, 'i16', -3599),
('i64', 0xF1F1F1F1F1F1F171, 'i8', 113),
('i64', 0xF1F1F1F1F1F1F1F1, 'i8', -15),
])

View File

@ -5,16 +5,18 @@ import pytest
from ..helpers import Suite
TYPE_LIST = [
'u8', 'u32', 'u64',
'i8', 'i32', 'i64',
'u8', 'u16', 'u32', 'u64',
'i8', 'i16', 'i32', 'i64',
'f32', 'f64',
]
BOUND_MAP = {
'u8': struct.unpack('<BB', bytes.fromhex('00FF')),
'u16': struct.unpack('<HH', bytes.fromhex('0000FFFF')),
'u32': struct.unpack('<II', bytes.fromhex('00000000FFFFFFFF')),
'u64': struct.unpack('<QQ', bytes.fromhex('0000000000000000FFFFFFFFFFFFFFFF')),
'i8': struct.unpack('<bb', bytes.fromhex('807F')),
'i16': struct.unpack('<hh', bytes.fromhex('0080FF7F')),
'i32': struct.unpack('<ii', bytes.fromhex('00000080FFFFFF7F')),
'i64': struct.unpack('<qq', bytes.fromhex('0000000000000080FFFFFFFFFFFFFF7F')),
'f32': struct.unpack('<ff', bytes.fromhex('01000000FFFF7F7F')),