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

Implement floating-point rounding in interpreter #344

Merged
merged 3 commits into from
Sep 17, 2020
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
8 changes: 8 additions & 0 deletions interpreter/binary/decode.ml
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,14 @@ let simd_prefix s =
| 0xcel -> i64x2_add
| 0xd1l -> i64x2_sub
| 0xd5l -> i64x2_mul
| 0xd8l -> f32x4_ceil
| 0xd9l -> f32x4_floor
| 0xdal -> f32x4_trunc
| 0xdbl -> f32x4_nearest
| 0xdcl -> f64x2_ceil
| 0xddl -> f64x2_floor
| 0xdel -> f64x2_trunc
| 0xdfl -> f64x2_nearest
| 0xe0l -> f32x4_abs
| 0xe1l -> f32x4_neg
| 0xe3l -> f32x4_sqrt
Expand Down
8 changes: 8 additions & 0 deletions interpreter/binary/encode.ml
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,14 @@ let encode m =
| Unary (V128 V128Op.(I32x4 WidenLowU)) -> simd_op 0xa9l
| Unary (V128 V128Op.(I32x4 WidenHighU)) -> simd_op 0xaal
| Unary (V128 V128Op.(I64x2 Neg)) -> simd_op 0xc1l
| Unary (V128 V128Op.(F32x4 Ceil)) -> simd_op 0xd8l
| Unary (V128 V128Op.(F32x4 Floor)) -> simd_op 0xd9l
| Unary (V128 V128Op.(F32x4 Trunc)) -> simd_op 0xdal
| Unary (V128 V128Op.(F32x4 Nearest)) -> simd_op 0xdbl
| Unary (V128 V128Op.(F64x2 Ceil)) -> simd_op 0xdcl
| Unary (V128 V128Op.(F64x2 Floor)) -> simd_op 0xddl
| Unary (V128 V128Op.(F64x2 Trunc)) -> simd_op 0xdel
| Unary (V128 V128Op.(F64x2 Nearest)) -> simd_op 0xdfl
| Unary (V128 V128Op.(F32x4 Abs)) -> simd_op 0xe0l
| Unary (V128 V128Op.(F32x4 Neg)) -> simd_op 0xe1l
| Unary (V128 V128Op.(F32x4 Sqrt)) -> simd_op 0xe3l
Expand Down
8 changes: 8 additions & 0 deletions interpreter/exec/eval_numeric.ml
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,19 @@ struct
| F32x4 Abs -> to_value (SXX.F32x4.abs (of_value 1 v))
| F32x4 Neg -> to_value (SXX.F32x4.neg (of_value 1 v))
| F32x4 Sqrt -> to_value (SXX.F32x4.sqrt (of_value 1 v))
| F32x4 Ceil -> to_value (SXX.F32x4.ceil (of_value 1 v))
| F32x4 Floor -> to_value (SXX.F32x4.floor (of_value 1 v))
| F32x4 Trunc -> to_value (SXX.F32x4.trunc (of_value 1 v))
| F32x4 Nearest -> to_value (SXX.F32x4.nearest (of_value 1 v))
| F32x4 ConvertI32x4S -> to_value (SXX.F32x4_convert.convert_i32x4_s (of_value 1 v))
| F32x4 ConvertI32x4U -> to_value (SXX.F32x4_convert.convert_i32x4_u (of_value 1 v))
| F64x2 Abs -> to_value (SXX.F64x2.abs (of_value 1 v))
| F64x2 Neg -> to_value (SXX.F64x2.neg (of_value 1 v))
| F64x2 Sqrt -> to_value (SXX.F64x2.sqrt (of_value 1 v))
| F64x2 Ceil -> to_value (SXX.F64x2.ceil (of_value 1 v))
| F64x2 Floor -> to_value (SXX.F64x2.floor (of_value 1 v))
| F64x2 Trunc -> to_value (SXX.F64x2.trunc (of_value 1 v))
| F64x2 Nearest -> to_value (SXX.F64x2.nearest (of_value 1 v))
| V128 Not -> to_value (SXX.V128.lognot (of_value 1 v))
| _ -> failwith "TODO v128 unimplemented unop"

Expand Down
8 changes: 8 additions & 0 deletions interpreter/exec/simd.ml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ sig
val abs : t -> t
val neg : t -> t
val sqrt : t -> t
val ceil : t -> t
val floor : t -> t
val trunc : t -> t
val nearest : t -> t
val add : t -> t -> t
val sub : t -> t -> t
val mul : t -> t -> t
Expand Down Expand Up @@ -254,6 +258,10 @@ struct
let abs = unop Float.abs
let neg = unop Float.neg
let sqrt = unop Float.sqrt
let ceil = unop Float.ceil
let floor = unop Float.floor
let trunc = unop Float.trunc
let nearest = unop Float.nearest
let add = binop Float.add
let sub = binop Float.sub
let mul = binop Float.mul
Expand Down
4 changes: 3 additions & 1 deletion interpreter/syntax/ast.ml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ struct
| Eq | Ne | LtS | LtU | LeS | LeU | GtS | GtU | GeS | GeU
| Swizzle | Shuffle of int list | NarrowS | NarrowU
| AddSatS | AddSatU | SubSatS | SubSatU
type funop = Abs | Neg | Sqrt | ConvertI32x4S | ConvertI32x4U
type funop = Abs | Neg | Sqrt
| Ceil | Floor | Trunc | Nearest
| ConvertI32x4S | ConvertI32x4U
type fbinop = Add | Sub | Mul | Div | Min | Max
| Eq | Ne | Lt | Le | Gt | Ge
type vunop = Not
Expand Down
8 changes: 8 additions & 0 deletions interpreter/syntax/operators.ml
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,10 @@ let f32x4_ge = Binary (V128 V128Op.(F32x4 Ge))
let f32x4_abs = Unary (V128 (V128Op.F32x4 V128Op.Abs))
let f32x4_neg = Unary (V128 (V128Op.F32x4 V128Op.Neg))
let f32x4_sqrt = Unary (V128 (V128Op.F32x4 V128Op.Sqrt))
let f32x4_ceil = Unary (V128 (V128Op.(F32x4 Ceil)))
let f32x4_floor = Unary (V128 (V128Op.(F32x4 Floor)))
let f32x4_trunc = Unary (V128 (V128Op.(F32x4 Trunc)))
let f32x4_nearest = Unary (V128 (V128Op.(F32x4 Nearest)))
let f32x4_add = Binary (V128 (V128Op.F32x4 V128Op.Add))
let f32x4_sub = Binary (V128 (V128Op.F32x4 V128Op.Sub))
let f32x4_mul = Binary (V128 (V128Op.F32x4 V128Op.Mul))
Expand All @@ -405,6 +409,10 @@ let f64x2_gt = Binary (V128 V128Op.(F64x2 Gt))
let f64x2_ge = Binary (V128 V128Op.(F64x2 Ge))
let f64x2_neg = Unary (V128 (V128Op.F64x2 V128Op.Neg))
let f64x2_sqrt = Unary (V128 (V128Op.F64x2 V128Op.Sqrt))
let f64x2_ceil = Unary (V128 (V128Op.(F64x2 Ceil)))
let f64x2_floor = Unary (V128 (V128Op.(F64x2 Floor)))
let f64x2_trunc = Unary (V128 (V128Op.(F64x2 Trunc)))
let f64x2_nearest = Unary (V128 (V128Op.(F64x2 Nearest)))
let f64x2_add = Binary (V128 (V128Op.F64x2 V128Op.Add))
let f64x2_sub = Binary (V128 (V128Op.F64x2 V128Op.Sub))
let f64x2_mul = Binary (V128 (V128Op.F64x2 V128Op.Mul))
Expand Down
8 changes: 8 additions & 0 deletions interpreter/text/arrange.ml
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,14 @@ struct
| I32x4 TruncSatF32x4S -> "i32x4.trunc_sat_f32x4_s"
| I32x4 TruncSatF32x4U -> "i32x4.trunc_sat_f32x4_u"
| I64x2 Neg -> "i64x2.neg"
| F32x4 Ceil -> "f32x4.ceil"
| F32x4 Floor -> "f32x4.floor"
| F32x4 Trunc -> "f32x4.trunc"
| F32x4 Nearest -> "f32x4.nearest"
| F64x2 Ceil -> "f64x2.ceil"
| F64x2 Floor -> "f64x2.floor"
| F64x2 Trunc -> "f64x2.trunc"
| F64x2 Nearest -> "f64x2.nearest"
| F32x4 Abs -> "f32x4.abs"
| F32x4 Neg -> "f32x4.neg"
| F32x4 Sqrt -> "f32x4.sqrt"
Expand Down
4 changes: 4 additions & 0 deletions interpreter/text/lexer.mll
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,10 @@ rule token = parse
| (simd_shape as s)".neg"
{ UNARY (simdop s i8x16_neg i16x8_neg i32x4_neg i64x2_neg f32x4_neg f64x2_neg) }
| (simd_float_shape as s)".sqrt" { UNARY (simd_float_op s f32x4_sqrt f64x2_sqrt) }
| (simd_float_shape as s)".ceil" { UNARY (simd_float_op s f32x4_ceil f64x2_ceil) }
| (simd_float_shape as s)".floor" { UNARY (simd_float_op s f32x4_floor f64x2_floor) }
| (simd_float_shape as s)".trunc" { UNARY (simd_float_op s f32x4_trunc f64x2_trunc) }
| (simd_float_shape as s)".nearest" { UNARY (simd_float_op s f32x4_nearest f64x2_nearest) }
| (simd_shape as s)".add"
{ BINARY (simdop s i8x16_add i16x8_add i32x4_add i64x2_add f32x4_add f64x2_add) }
| (simd_shape as s)".sub"
Expand Down
2 changes: 2 additions & 0 deletions test/core/simd/meta/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Currently it only support following simd test files generation.
- 'simd_i16x8_sat_arith.wast'
- 'simd_f32x4.wast'
- 'simd_f64x2.wast'
- 'simd_f32x4_rounding'
- 'simd_f64x2_rounding'


Usage:
Expand Down
4 changes: 3 additions & 1 deletion test/core/simd/meta/gen_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
'simd_f32x4',
'simd_f64x2',
'simd_int_arith2',
'simd_f32x4_rounding',
'simd_f64x2_rounding',
)


Expand Down Expand Up @@ -61,4 +63,4 @@ def main():

if __name__ == '__main__':
main()
print('Done.')
print('Done.')
85 changes: 85 additions & 0 deletions test/core/simd/meta/simd_f32x4_rounding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env python3

"""
Generate f32x4 [ceil, floor, trunc, nearest] cases.
"""

from simd_f32x4_arith import Simdf32x4ArithmeticCase
from simd_float_op import FloatingPointRoundingOp
from simd import SIMD
from test_assert import AssertReturn


class Simdf32x4RoundingCase(Simdf32x4ArithmeticCase):
UNARY_OPS = ('ceil', 'floor', 'trunc', 'nearest')
BINARY_OPS = ()
floatOp = FloatingPointRoundingOp()

def get_combine_cases(self):
return ''

def get_normal_case(self):
"""Normal test cases from WebAssembly core tests.
"""
cases = []
unary_test_data = []

for op in self.UNARY_OPS:
op_name = self.full_op_name(op)
for operand in self.FLOAT_NUMBERS:
result = self.floatOp.unary_op(op, operand)
if 'nan' in result:
unary_test_data.append([op_name, operand, 'nan:canonical'])
else:
unary_test_data.append([op_name, operand, result])

for operand in self.LITERAL_NUMBERS:
result = self.floatOp.unary_op(op, operand, hex_form=False)
unary_test_data.append([op_name, operand, result])

for operand in self.NAN_NUMBERS:
if 'nan:' in operand:
unary_test_data.append([op_name, operand, 'nan:arithmetic'])
else:
unary_test_data.append([op_name, operand, 'nan:canonical'])

for case in unary_test_data:
cases.append(str(AssertReturn(case[0],
[SIMD.v128_const(elem, self.LANE_TYPE) for elem in case[1:-1]],
SIMD.v128_const(case[-1], self.LANE_TYPE))))

self.get_unknown_operator_case(cases)

return '\n'.join(cases)

def get_unknown_operator_case(self, cases):
"""Unknown operator cases.
"""

tpl_assert = "(assert_malformed (module quote \"(memory 1) (func (result v128) " \
"({lane_type}.{op} {value}))\") \"unknown operator\")"

unknown_op_cases = ['\n\n;; Unknown operators\n']
cases.extend(unknown_op_cases)

for lane_type in ['i8x16', 'i16x8', 'i32x4', 'i64x2']:
for op in self.UNARY_OPS:
cases.append(tpl_assert.format(lane_type=lane_type, op=op, value=self.v128_const('i32x4', '0')))

def gen_test_cases(self):
wast_filename = '../simd_{lane_type}_rounding.wast'.format(lane_type=self.LANE_TYPE)
with open(wast_filename, 'w') as fp:
txt_test_case = self.get_all_cases()
txt_test_case = txt_test_case.replace(
self.LANE_TYPE + ' arithmetic',
self.LANE_TYPE + ' [ceil, floor, trunc, nearest]')
fp.write(txt_test_case)


def gen_test_cases():
simd_f32x4_case = Simdf32x4RoundingCase()
simd_f32x4_case.gen_test_cases()


if __name__ == '__main__':
gen_test_cases()
29 changes: 29 additions & 0 deletions test/core/simd/meta/simd_f64x2_rounding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env python3

"""
Generate f64x2 [ceil, floor, trunc, nearest] cases.
"""

from simd_f32x4_rounding import Simdf32x4RoundingCase
from simd_f64x2 import Simdf64x2Case
from simd_f64x2_arith import Simdf64x2ArithmeticCase
from simd_float_op import FloatingPointRoundingOp
from simd import SIMD
from test_assert import AssertReturn


class Simdf64x2RoundingCase(Simdf32x4RoundingCase):

LANE_TYPE = 'f64x2'
FLOAT_NUMBERS = Simdf64x2ArithmeticCase.FLOAT_NUMBERS
LITERAL_NUMBERS = Simdf64x2ArithmeticCase.LITERAL_NUMBERS
NAN_NUMBERS = Simdf64x2ArithmeticCase.NAN_NUMBERS


def gen_test_cases():
simd_f64x2_case = Simdf64x2RoundingCase()
simd_f64x2_case.gen_test_cases()


if __name__ == '__main__':
gen_test_cases()
51 changes: 50 additions & 1 deletion test/core/simd/meta/simd_float_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,53 @@ def binary_op(self, op: str, p1: str, p2: str) -> str:
elif op == 'ge':
return '-1' if f1 >= f2 else '0'
else:
raise Exception('Unknown binary operation')
raise Exception('Unknown binary operation')


class FloatingPointRoundingOp(FloatingPointOp):
def unary_op(self, op: str, p1: str, hex_form=True) -> str:
"""Unnary operation on p1 with the operation specified by op

:param op: ceil, floor, trunc, nearest
:param p1: float number in hex
:return:
"""
if '0x' in p1:
f1 = float.fromhex(p1)
else:
f1 = float(p1)

if 'nan' in p1:
return 'nan'

if 'inf' in p1:
return p1

# The rounding ops don't treat -0.0 correctly, e.g.:
# math.ceil(-0.4) returns +0.0, so copy the sign.
elif op == 'ceil':
r = math.copysign(math.ceil(f1), f1)
if hex_form:
return r.hex()
else:
return str(r)
elif op == 'floor':
r = math.copysign(math.floor(f1), f1)
if hex_form:
return r.hex()
else:
return str(r)
elif op == 'trunc':
r = math.copysign(math.trunc(f1), f1)
if hex_form:
return r.hex()
else:
return str(r)
elif op == 'nearest':
r = math.copysign(round(f1), f1)
if hex_form:
return r.hex()
else:
return str(r)
else:
raise Exception('Unknown binary operation')
Loading