Browse Source

Meta: Generate bigint values for v128 constants in Wasm tests

Ali Mohammad Pur 2 years ago
parent
commit
5f013e5374
2 changed files with 93 additions and 3 deletions
  1. 86 1
      Meta/generate-libwasm-spec-test.py
  2. 7 2
      Tests/LibWasm/test-wasm.cpp

+ 86 - 1
Meta/generate-libwasm-spec-test.py

@@ -8,6 +8,7 @@ import math
 from tempfile import NamedTemporaryFile
 from subprocess import call
 import json
+import array
 
 atom_end = set('()"' + whitespace)
 
@@ -61,8 +62,89 @@ def parse_typed_value(ast):
         'i64.const': 'i64',
         'f32.const': 'float',
         'f64.const': 'double',
+        'v128.const': 'bigint',
     }
-    if len(ast) == 2 and ast[0][0] in types:
+
+    v128_sizes = {
+        'i8x16': 1,
+        'i16x8': 2,
+        'i32x4': 4,
+        'i64x2': 8,
+        'f32x4': 4,
+        'f64x2': 8,
+    }
+    v128_format_names = {
+        'i8x16': 'b',
+        'i16x8': 'h',
+        'i32x4': 'i',
+        'i64x2': 'q',
+        'f32x4': 'f',
+        'f64x2': 'd',
+    }
+    v128_format_names_unsigned = {
+        'i8x16': 'B',
+        'i16x8': 'H',
+        'i32x4': 'I',
+        'i64x2': 'Q',
+    }
+
+    def parse_v128_chunk(num, type) -> array:
+        negative = 1
+        if num.startswith('-'):
+            negative = -1
+            num = num[1:]
+        elif num.startswith('+'):
+            num = num[1:]
+
+        # wtf spec test, split your wast tests already
+        while num.startswith('0') and not num.startswith('0x'):
+            num = num[1:]
+
+        if num == '':
+            num = '0'
+
+        if type.startswith('f'):
+            def generate():
+                if num == 'nan:canonical':
+                    return float.fromhex('0x7fc00000')
+                if num == 'nan:arithmetic':
+                    return float.fromhex('0x7ff00000')
+                if num == 'nan:signaling':
+                    return float.fromhex('0x7ff80000')
+                if num.startswith('nan:'):
+                    # FIXME: I have no idea if this is actually correct :P
+                    rest = num[4:]
+                    return float.fromhex('0x7ff80000') + int(rest, base=16)
+                if num.lower() == 'infinity':
+                    return float.fromhex('0x7ff00000') * negative
+                try:
+                    return float(num) * negative
+                except ValueError:
+                    return float.fromhex(num) * negative
+
+            value = generate()
+            return struct.pack(f'={v128_format_names[type]}', value)
+        value = negative * int(num.replace('_', ''), base=0)
+        try:
+            return struct.pack(f'={v128_format_names[type]}', value)
+        except struct.error:
+            # The test format uses signed and unsigned values interchangeably, this is probably an unsigned value.
+            return struct.pack(f'={v128_format_names_unsigned[type]}', value)
+
+    if len(ast) >= 2 and ast[0][0] in types:
+        if ast[0][0] == 'v128.const':
+            value = array.array('b')
+            for i, num in enumerate(ast[2:]):
+                size = v128_sizes[ast[1][0]]
+                s = len(value)
+                value.frombytes(parse_v128_chunk(num[0], ast[1][0]))
+                assert len(value) - s == size, f'Expected {size} bytes, got {len(value) - s} bytes'
+
+            return {
+                'type': types[ast[0][0]],
+                'value': value.tobytes().hex()
+            }
+
         return {"type": types[ast[0][0]], "value": ast[1][0]}
 
     return {"type": "error"}
@@ -285,6 +367,9 @@ def genarg(spec):
 
     def gen():
         x = spec['value']
+        if spec['type'] == 'bigint':
+            return f"0x{x}n"
+
         if spec['type'] in ('i32', 'i64'):
             if x.startswith('0x'):
                 if spec['type'] == 'i32':

+ 7 - 2
Tests/LibWasm/test-wasm.cpp

@@ -235,7 +235,9 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke)
             }
 
             u128 bits;
-            (void)argument.as_bigint().big_integer().export_data({ bit_cast<u8*>(&bits), sizeof(bits) });
+            (void)argument.as_bigint().big_integer().unsigned_value().export_data({ bit_cast<u8*>(&bits), sizeof(bits) });
+            VERIFY(!argument.as_bigint().big_integer().is_negative());
+
             arguments.append(Wasm::Value(bits));
             break;
         }
@@ -269,7 +271,10 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke)
             [](auto const& value) { return JS::Value(static_cast<double>(value)); },
             [](i32 value) { return JS::Value(static_cast<double>(value)); },
             [&](i64 value) { return JS::Value(JS::BigInt::create(vm, Crypto::SignedBigInteger { value })); },
-            [&](u128 value) { return JS::Value(JS::BigInt::create(vm, Crypto::SignedBigInteger::import_data(bit_cast<u8 const*>(&value), sizeof(value)))); },
+            [&](u128 value) {
+                auto unsigned_bigint_value = Crypto::UnsignedBigInteger::import_data(bit_cast<u8 const*>(&value), sizeof(value));
+                return JS::Value(JS::BigInt::create(vm, Crypto::SignedBigInteger(move(unsigned_bigint_value), false)));
+            },
             [](Wasm::Reference const& reference) {
                 return reference.ref().visit(
                     [](const Wasm::Reference::Null&) { return JS::js_null(); },