Skip to content
This repository was archived by the owner on Dec 22, 2021. It is now read-only.

[interpreter] Implement i32x4.trunc_sat_f64x2_{s,u}_zero #466

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions interpreter/binary/decode.ml
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,8 @@ let simd_prefix s =
| 0x50l -> v128_or
| 0x51l -> v128_xor
| 0x52l -> v128_bitselect
| 0x55l -> i32x4_trunc_sat_f64x2_s_zero
| 0x56l -> i32x4_trunc_sat_f64x2_u_zero
| 0x58l ->
let a, o = memop s in
let lane = u8 s in
Expand Down
2 changes: 2 additions & 0 deletions interpreter/binary/encode.ml
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,8 @@ let encode m =
| Unary (V128 V128Op.(F64x2 Sqrt)) -> simd_op 0xefl
| Unary (V128 V128Op.(I32x4 TruncSatF32x4S)) -> simd_op 0xf8l
| Unary (V128 V128Op.(I32x4 TruncSatF32x4U)) -> simd_op 0xf9l
| Unary (V128 V128Op.(I32x4 TruncSatF64x2SZero)) -> simd_op 0x55l
| Unary (V128 V128Op.(I32x4 TruncSatF64x2UZero)) -> simd_op 0x56l
| Unary (V128 V128Op.(F32x4 ConvertI32x4S)) -> simd_op 0xfal
| Unary (V128 V128Op.(F32x4 ConvertI32x4U)) -> simd_op 0xfbl
| Unary (V128 _) -> failwith "unimplemented V128 Unary op"
Expand Down
4 changes: 4 additions & 0 deletions interpreter/exec/eval_simd.ml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ module SimdOp (SXX : Simd.S) (Value : ValueType with type t = SXX.t) = struct
| I32x4 WidenHighU -> to_value (SXX.I32x4_convert.widen_high_u (of_value 1 v))
| I32x4 TruncSatF32x4S -> to_value (SXX.I32x4_convert.trunc_sat_f32x4_s (of_value 1 v))
| I32x4 TruncSatF32x4U -> to_value (SXX.I32x4_convert.trunc_sat_f32x4_u (of_value 1 v))
| I32x4 TruncSatF64x2SZero ->
to_value (SXX.I32x4_convert.trunc_sat_f64x2_s_zero (of_value 1 v))
| I32x4 TruncSatF64x2UZero ->
to_value (SXX.I32x4_convert.trunc_sat_f64x2_u_zero (of_value 1 v))
| I64x2 Abs -> to_value (SXX.I64x2.abs (of_value 1 v))
| I64x2 Neg -> to_value (SXX.I64x2.neg (of_value 1 v))
| I64x2 WidenLowS -> to_value (SXX.I64x2_convert.widen_low_s (of_value 1 v))
Expand Down
7 changes: 7 additions & 0 deletions interpreter/exec/simd.ml
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ sig
module I32x4_convert : sig
val trunc_sat_f32x4_s : t -> t
val trunc_sat_f32x4_u : t -> t
val trunc_sat_f64x2_s_zero : t -> t
val trunc_sat_f64x2_u_zero : t -> t
val widen_low_s : t -> t
val widen_high_s : t -> t
val widen_low_u : t -> t
Expand Down Expand Up @@ -447,6 +449,11 @@ struct
let trunc_sat_f32x4_s = convert I32_convert.trunc_sat_f32_s
let trunc_sat_f32x4_u = convert I32_convert.trunc_sat_f32_u

let convert_zero f v =
Rep.(of_i32x4 I32.(zero :: zero :: (List.map f (to_f64x2 v)))
let trunc_sat_f64x2_s_zero = convert_zero I32_convert.trunc_sat_f64_s
let trunc_sat_f64x2_u_zero = convert_zero I32_convert.trunc_sat_f64_u

let widen take_or_drop mask x =
Rep.of_i32x4 (List.map (Int32.logand mask) (take_or_drop 4 (Rep.to_i16x8 x)))
let widen_low_s = widen Lib.List.take 0xffffffffl
Expand Down
2 changes: 1 addition & 1 deletion interpreter/syntax/ast.ml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ module SimdOp =
struct
type iunop = Abs | Neg | TruncSatF32x4S | TruncSatF32x4U
| WidenLowS | WidenLowU | WidenHighS | WidenHighU
| Popcnt
| Popcnt | TruncSatF64x2SZero | TruncSatF64x2UZero
type ibinop = Add | Sub | MinS | MinU | MaxS | MaxU | Mul | AvgrU
| Eq | Ne | LtS | LtU | LeS | LeU | GtS | GtU | GeS | GeU
| Swizzle | Shuffle of int list | NarrowS | NarrowU
Expand Down
2 changes: 2 additions & 0 deletions interpreter/syntax/operators.ml
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,8 @@ let i32x4_max_u = Binary (V128 V128Op.(I32x4 MaxU))
let i32x4_mul = Binary (V128 V128Op.(I32x4 Mul))
let i32x4_trunc_sat_f32x4_s = Unary (V128 V128Op.(I32x4 TruncSatF32x4S))
let i32x4_trunc_sat_f32x4_u = Unary (V128 V128Op.(I32x4 TruncSatF32x4U))
let i32x4_trunc_sat_f64x2_s_zero = Unary (V128 V128Op.(I32x4 TruncSatF64x2SZero))
let i32x4_trunc_sat_f64x2_u_zero = Unary (V128 V128Op.(I32x4 TruncSatF64x2UZero))
let i32x4_dot_i16x8_s = Binary (V128 V128Op.(I32x4 DotI16x8S))
let i32x4_extmul_low_i16x8_s = Binary (V128 V128Op.(I32x4 ExtMulLowS))
let i32x4_extmul_high_i16x8_s = Binary (V128 V128Op.(I32x4 ExtMulHighS))
Expand Down
2 changes: 2 additions & 0 deletions interpreter/text/arrange.ml
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ struct
| I32x4 WidenHighU -> "i32x4.widen_high_i16x8_u"
| I32x4 TruncSatF32x4S -> "i32x4.trunc_sat_f32x4_s"
| I32x4 TruncSatF32x4U -> "i32x4.trunc_sat_f32x4_u"
| I32x4 TruncSatF64x2SZero -> "i32x4.trunc_sat_f64x2_s_zero"
| I32x4 TruncSatF64x2UZero -> "i32x4.trunc_sat_f64x2_u_zero"
| I64x2 Abs -> "i64x2.abs"
| I64x2 Neg -> "i64x2.neg"
| I64x2 WidenLowS -> "i64x2.widen_low_i32x4_s"
Expand Down
2 changes: 2 additions & 0 deletions interpreter/text/lexer.mll
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,8 @@ rule token = parse
UNARY (simd_int_op s i8x16_avgr_u i16x8_avgr_u unreachable unreachable) }
| "i32x4.trunc_sat_f32x4_"(sign as s)
{ UNARY (ext s i32x4_trunc_sat_f32x4_s i32x4_trunc_sat_f32x4_u) }
| "i32x4.trunc_sat_f64x2_"(sign as s)"_zero"
{ UNARY (ext s i32x4_trunc_sat_f64x2_s_zero i32x4_trunc_sat_f64x2_u_zero) }
| "f32x4.convert_i32x4_"(sign as s)
{ UNARY (ext s f32x4_convert_i32x4_s f32x4_convert_i32x4_u) }
| "i8x16.narrow_i16x8_"(sign as s)
Expand Down
1 change: 1 addition & 0 deletions test/core/simd/meta/gen_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
'simd_store_lane',
'simd_ext_mul',
'simd_int_to_int_widen',
'simd_int_trunc_sat_float',
'simd_i16x8_q15mulr_sat_s',
)

Expand Down
62 changes: 23 additions & 39 deletions test/core/simd/meta/simd_float_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ class FloatingPointOp:
def binary_op(self, op: str, p1: str, p2: str) -> str:
pass

def of_string(self, value: str) -> float:
if '0x' in value:
return float.fromhex(value)
else:
return float(value)

def is_hex(self, value:str) -> bool:
return '0x' in value

def to_single_precision(self, value: float) -> str:
# Python only has doubles, when reading in float, we need to convert to
# single-precision first.
return struct.unpack('f', struct.pack('f', value))[0]


class FloatingPointArithOp(FloatingPointOp):
"""Common arithmetic ops for both f32x4 and f64x2:
Expand All @@ -29,19 +43,9 @@ def binary_op(self, op: str, p1: str, p2: str, single_prec=False) -> str:
:param p2: float number in hex
:return:
"""
if '0x' in p1 or '0x' in p2:
hex_form = True
else:
hex_form = False

if '0x' in p1:
f1 = float.fromhex(p1)
else:
f1 = float(p1)
if '0x' in p2:
f2 = float.fromhex(p2)
else:
f2 = float(p2)
hex_form = self.is_hex(p1) or self.is_hex(p2)
f1 = self.of_string(p1)
f2 = self.of_string(p2)

if op == 'add':
if 'inf' in p1 and 'inf' in p2 and p1 != p2:
Expand Down Expand Up @@ -145,15 +149,8 @@ def binary_op(self, op: str, p1: str, p2: str, hex_form=True) -> str:
:param p2: float number in hex
:return:
"""
if '0x' in p1:
f1 = float.fromhex(p1)
else:
f1 = float(p1)

if '0x' in p2:
f2 = float.fromhex(p2)
else:
f2 = float(p2)
f1 = self.of_string(p1)
f2 = self.of_string(p2)

if '-nan' in [p1, p2] and 'nan' in [p1, p2]:
return p1
Expand Down Expand Up @@ -205,10 +202,7 @@ def unary_op(self, op: str, p1: str, hex_form=True) -> str:
:param p1: float number in hex
:return:
"""
if '0x' in p1:
f1 = float.fromhex(p1)
else:
f1 = float(p1)
f1 = self.of_string(p1)
if op == 'abs':
if hex_form:
return abs(f1).hex()
Expand Down Expand Up @@ -239,15 +233,8 @@ def binary_op(self, op: str, p1: str, p2: str) -> str:
if 'nan' in p1.lower() or 'nan' in p2.lower():
return '0'

if '0x' in p1:
f1 = float.fromhex(p1)
else:
f1 = float(p1)

if '0x' in p2:
f2 = float.fromhex(p2)
else:
f2 = float(p2)
f1 = self.of_string(p1)
f2 = self.of_string(p2)

if op == 'eq':
return '-1' if f1 == f2 else '0'
Expand Down Expand Up @@ -278,10 +265,7 @@ def unary_op(self, op: str, p1: str, hex_form=True) -> str:
:param p1: float number in hex
:return:
"""
if '0x' in p1:
f1 = float.fromhex(p1)
else:
f1 = float(p1)
f1 = self.of_string(p1)

if 'nan' in p1:
return 'nan'
Expand Down
178 changes: 178 additions & 0 deletions test/core/simd/meta/simd_int_trunc_sat_float.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#!/usr/bin/env python3

"""Base class for generating SIMD <int>.trun_sat_<float> test cases.
Subclasses should set:
- LANE_TYPE
- SRC_LANE_TYPE
- UNARY_OPS
"""

from abc import abstractmethod
import struct
from math import trunc
from simd import SIMD
from simd_arithmetic import SimdArithmeticCase
from test_assert import AssertReturn
from simd_float_op import FloatingPointOp, FloatingPointRoundingOp
from simd_integer_op import ArithmeticOp


class SimdConversionCase(SimdArithmeticCase):
BINARY_OPS = ()
TEST_FUNC_TEMPLATE_HEADER = ";; Tests for {} trunc sat conversions from float.\n"

def is_signed(self, op):
return op.endswith("_s") or op.endswith("_s_zero")

def get_test_data(self, lane):
return [
"0.0",
"-0.0",
"1.5",
"-1.5",
"1.9",
"2.0",
"-1.9",
"-2.0",
str(float(lane.max - 127)),
str(float(-(lane.max - 127))),
str(float(lane.max + 1)),
str(float(-(lane.max + 1))),
str(float(lane.max * 2)),
str(float(-(lane.max * 2))),
str(float(lane.max)),
str(float(-lane.max)),
str(float(lane.mask - 1)),
str(float(lane.mask)),
str(float(lane.mask + 1)),
"0x1p-149",
"-0x1p-149",
"0x1p-126",
"-0x1p-126",
"0x1p-1",
"-0x1p-1",
"0x1p+0",
"-0x1p+0",
"0x1.19999ap+0",
"-0x1.19999ap+0",
"0x1.921fb6p+2",
"-0x1.921fb6p+2",
"0x1.fffffep+127",
"-0x1.fffffep+127",
"0x1.ccccccp-1",
"-0x1.ccccccp-1",
"0x1.fffffep-1",
"-0x1.fffffep-1",
"0x1.921fb6p+2",
"-0x1.921fb6p+2",
"0x1.fffffep+127",
"-0x1.fffffep+127",
"+inf",
"-inf",
"+nan",
"-nan",
"nan:0x444444",
"-nan:0x444444",
"42",
"-42",
"0123456792.0",
"01234567890.0",
]

def to_float_precision(self, value):
# Python supports double precision, so given an an input that cannot be
# precisely represented in f32, we need to round it.
return value

@abstractmethod
def to_results(self, result: str):
# Subclasses can override this to set the shape of the results. This is
# useful if instructions zero top lanes.
pass

def conversion_op(self, op, operand):
fop = FloatingPointRoundingOp()
signed = self.is_signed(op)
sat_op = ArithmeticOp("sat_s") if signed else ArithmeticOp("sat_u")
result = fop.unary_op("trunc", operand, hex_form=False)
if result == "nan":
return "0"
elif result == "+inf":
return str(str(self.lane.max) if signed else str(self.lane.mask))
elif result == "-inf":
return str(self.lane.min if signed else 0)
else:
float_result = self.to_float_precision(float(result))
trunced = int(trunc(float_result))
saturated = sat_op.unary_op(trunced, self.lane)
return str(saturated)

def get_case_data(self):
test_data = []
for op in self.UNARY_OPS:
op_name = "{}.{}".format(self.LANE_TYPE, op)
test_data.append(["#", op_name])

for operand in self.get_test_data(self.lane):
operand = str(operand)
if "nan" in operand:
test_data.append(
[op_name, [operand], "0", [self.SRC_LANE_TYPE, self.LANE_TYPE]]
)
else:
result = self.conversion_op(op_name, operand)
results = self.to_results(result)
assert "nan" not in result
test_data.append(
[
op_name,
[operand],
results,
[self.SRC_LANE_TYPE, self.LANE_TYPE],
]
)

return test_data

def gen_test_cases(self):
wast_filename = "../simd_{}_trunc_sat_{}.wast".format(
self.LANE_TYPE, self.SRC_LANE_TYPE
)
with open(wast_filename, "w") as fp:
fp.write(self.get_all_cases())

def get_combine_cases(self):
return ""


class SimdI32x4TruncSatF32x4Case(SimdConversionCase):
LANE_TYPE = "i32x4"
SRC_LANE_TYPE = "f32x4"
UNARY_OPS = ("trunc_sat_f32x4_s", "trunc_sat_f32x4_u")

def to_float_precision(self, value):
fop = FloatingPointOp()
return fop.to_single_precision(value)

def to_results(self, value: str):
return [value]


class SimdI32x4TruncSatF64x2Case(SimdConversionCase):
LANE_TYPE = "i32x4"
SRC_LANE_TYPE = "f64x2"
UNARY_OPS = ("trunc_sat_f64x2_s_zero", "trunc_sat_f64x2_u_zero")

def to_results(self, value: str):
return ["0", value]


def gen_test_cases():
i32x4_trunc_sat = SimdI32x4TruncSatF32x4Case()
i32x4_trunc_sat.gen_test_cases()
i32x4_trunc_sat = SimdI32x4TruncSatF64x2Case()
i32x4_trunc_sat.gen_test_cases()


if __name__ == "__main__":
gen_test_cases()
6 changes: 6 additions & 0 deletions test/core/simd/meta/simd_integer_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ def unary_op(self, operand, lane):
elif self.op == 'popcnt':
result = self.get_valid_value(v, lane)
return str(bin(result % lane.mod).count('1'))
elif self.op == 'sat_s':
# Don't call get_valid_value, it will truncate results.
return max(lane.min, min(v, lane.max))
elif self.op == 'sat_u':
# Don't call get_valid_value, it will truncate results.
return max(0, min(v, lane.mask))
else:
raise Exception('Unknown unary operation')

Expand Down
Loading