diff --git a/cutils.h b/cutils.h index aefb4dd..125f5f6 100644 --- a/cutils.h +++ b/cutils.h @@ -28,6 +28,7 @@ #include #include #include +#include #if defined(_WIN32) #include @@ -356,6 +357,81 @@ static inline void inplace_bswap32(uint8_t *tab) { put_u32(tab, bswap32(get_u32(tab))); } +static inline double fromfp16(uint16_t v) { + double d, s; + int e; + if ((v & 0x7C00) == 0x7C00) { + d = (v & 0x3FF) ? NAN : INFINITY; + } else { + d = (v & 0x3FF) / 1024.; + e = (v & 0x7C00) >> 10; + if (e == 0) { + e = -14; + } else { + d += 1; + e -= 15; + } + d = scalbn(d, e); + } + s = (v & 0x8000) ? -1.0 : 1.0; + return d * s; +} + +static inline uint16_t tofp16(double d) { + uint16_t f, s; + double t; + int e; + s = 0; + if (copysign(1, d) < 0) { // preserve sign when |d| is negative zero + d = -d; + s = 0x8000; + } + if (isinf(d)) + return s | 0x7C00; + if (isnan(d)) + return s | 0x7C01; + if (d == 0) + return s | 0; + d = 2 * frexp(d, &e); + e--; + if (e > 15) + return s | 0x7C00; // out of range, return +/-infinity + if (e < -25) { + d = 0; + e = 0; + } else if (e < -14) { + d = scalbn(d, e + 14); + e = 0; + } else { + d -= 1; + e += 15; + } + d *= 1024.; + f = (uint16_t)d; + t = d - f; + if (t < 0.5) + goto done; + if (t == 0.5) + if ((f & 1) == 0) + goto done; + // adjust for rounding + if (++f == 1024) { + f = 0; + if (++e == 31) + return s | 0x7C00; // out of range, return +/-infinity + } +done: + return s | (e << 10) | f; +} + +static inline int isfp16nan(uint16_t v) { + return (v & 0x7FFF) > 0x7C00; +} + +static inline int isfp16zero(uint16_t v) { + return (v & 0x7FFF) == 0; +} + /* XXX: should take an extra argument to pass slack information to the caller */ typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size); diff --git a/gen/function_source.c b/gen/function_source.c index 85705da..494f560 100644 Binary files a/gen/function_source.c and b/gen/function_source.c differ diff --git a/gen/hello.c b/gen/hello.c index e131cfa..3225da4 100644 Binary files a/gen/hello.c and b/gen/hello.c differ diff --git a/gen/hello_module.c b/gen/hello_module.c index 98a6765..7005c6f 100644 Binary files a/gen/hello_module.c and b/gen/hello_module.c differ diff --git a/gen/repl.c b/gen/repl.c index 9bdd1f8..df1a3cd 100644 Binary files a/gen/repl.c and b/gen/repl.c differ diff --git a/gen/test_fib.c b/gen/test_fib.c index 53c431b..f3b0352 100644 Binary files a/gen/test_fib.c and b/gen/test_fib.c differ diff --git a/quickjs-atom.h b/quickjs-atom.h index 01d1910..2174f1e 100644 --- a/quickjs-atom.h +++ b/quickjs-atom.h @@ -202,6 +202,7 @@ DEF(Int32Array, "Int32Array") DEF(Uint32Array, "Uint32Array") DEF(BigInt64Array, "BigInt64Array") DEF(BigUint64Array, "BigUint64Array") +DEF(Float16Array, "Float16Array") DEF(Float32Array, "Float32Array") DEF(Float64Array, "Float64Array") DEF(DataView, "DataView") diff --git a/quickjs.c b/quickjs.c index 9132031..7883095 100644 --- a/quickjs.c +++ b/quickjs.c @@ -141,6 +141,7 @@ enum { JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */ JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */ JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT16_ARRAY, /* u.array (typed_array) */ JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */ JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */ JS_CLASS_DATAVIEW, /* u.typed_array */ @@ -944,6 +945,7 @@ struct JSObject { uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */ int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */ uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */ + uint16_t *fp16_ptr; /* JS_CLASS_FLOAT16_ARRAY */ float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */ double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */ } u; @@ -1568,6 +1570,7 @@ static JSClassShortDef const js_std_class_def[] = { { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */ { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */ { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */ + { JS_ATOM_Float16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT16_ARRAY */ { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */ { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */ { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */ @@ -4773,6 +4776,7 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas case JS_CLASS_UINT32_ARRAY: case JS_CLASS_BIG_INT64_ARRAY: case JS_CLASS_BIG_UINT64_ARRAY: + case JS_CLASS_FLOAT16_ARRAY: case JS_CLASS_FLOAT32_ARRAY: case JS_CLASS_FLOAT64_ARRAY: p->is_exotic = 1; @@ -6085,6 +6089,7 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */ case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */ case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_FLOAT16_ARRAY: /* u.typed_array / u.array */ case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */ case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */ case JS_CLASS_DATAVIEW: /* u.typed_array */ @@ -8004,6 +8009,10 @@ static BOOL js_get_fast_array_element(JSContext *ctx, JSObject *p, if (unlikely(idx >= p->u.array.count)) return FALSE; *pval = JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]); return TRUE; + case JS_CLASS_FLOAT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_float64(fromfp16(p->u.array.u.fp16_ptr[idx])); + return TRUE; case JS_CLASS_FLOAT32_ARRAY: if (unlikely(idx >= p->u.array.count)) return FALSE; *pval = js_float64(p->u.array.u.float_ptr[idx]); @@ -8837,6 +8846,13 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValue this_obj, p->u.array.u.uint64_ptr[idx] = v; } break; + case JS_CLASS_FLOAT16_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.fp16_ptr[idx] = tofp16(d); + break; case JS_CLASS_FLOAT32_ARRAY: if (JS_ToFloat64Free(ctx, &d, val)) goto ta_cvt_fail; @@ -11633,6 +11649,7 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) case JS_CLASS_UINT32_ARRAY: case JS_CLASS_BIG_INT64_ARRAY: case JS_CLASS_BIG_UINT64_ARRAY: + case JS_CLASS_FLOAT16_ARRAY: case JS_CLASS_FLOAT32_ARRAY: case JS_CLASS_FLOAT64_ARRAY: { @@ -41662,6 +41679,11 @@ static JSValue js_math_hypot(JSContext *ctx, JSValue this_val, return js_float64(r); } +static double js_math_f16round(double a) +{ + return fromfp16(tofp16(a)); +} + static double js_math_fround(double a) { return (float)a; @@ -41793,6 +41815,7 @@ static const JSCFunctionListEntry js_math_funcs[] = { JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, js_math_cbrt ), JS_CFUNC_DEF("hypot", 2, js_math_hypot ), JS_CFUNC_DEF("random", 0, js_math_random ), + JS_CFUNC_SPECIAL_DEF("f16round", 1, f_f, js_math_f16round ), JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ), JS_CFUNC_DEF("imul", 2, js_math_imul ), JS_CFUNC_DEF("clz32", 1, js_math_clz32 ), @@ -49485,8 +49508,8 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = { 0, 0, 0, 1, 1, 2, 2, - 3, 3, /* BigInt64Array, BigUint64Array */ - 2, 3 + 3, 3, // BigInt64Array, BigUint64Array + 1, 2, 3 // Float16Array, Float32Array, Float64Array }; static JSValue js_array_buffer_constructor3(JSContext *ctx, @@ -50199,6 +50222,8 @@ static JSValue js_typed_array_at(JSContext *ctx, JSValue this_val, return js_int32(p->u.array.u.int32_ptr[idx]); case JS_CLASS_UINT32_ARRAY: return js_uint32(p->u.array.u.uint32_ptr[idx]); + case JS_CLASS_FLOAT16_ARRAY: + return js_float64(fromfp16(p->u.array.u.fp16_ptr[idx])); case JS_CLASS_FLOAT32_ARRAY: return js_float64(p->u.array.u.float_ptr[idx]); case JS_CLASS_FLOAT64_ARRAY: @@ -50518,7 +50543,9 @@ static JSValue js_typed_array_fill(JSContext *ctx, JSValue this_val, double d; if (JS_ToFloat64(ctx, &d, argv[0])) return JS_EXCEPTION; - if (p->class_id == JS_CLASS_FLOAT32_ARRAY) { + if (p->class_id == JS_CLASS_FLOAT16_ARRAY) { + v64 = tofp16(d); + } else if (p->class_id == JS_CLASS_FLOAT32_ARRAY) { union { float f; uint32_t u32; @@ -50649,6 +50676,7 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValue this_val, int64_t v64; double d; float f; + uint16_t hf; len = js_typed_array_get_length_internal(ctx, this_val); if (len < 0) @@ -50797,6 +50825,39 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValue this_val, } } break; + case JS_CLASS_FLOAT16_ARRAY: + if (is_bigint) + break; + if (isnan(d)) { + const uint16_t *pv = p->u.array.u.fp16_ptr; + /* special case: indexOf returns -1, includes finds NaN */ + if (special != special_includes) + goto done; + for (; k != stop; k += inc) { + if (isfp16nan(pv[k])) { + res = k; + break; + } + } + } else if (d == 0) { + // special case: includes also finds negative zero + const uint16_t *pv = p->u.array.u.fp16_ptr; + for (; k != stop; k += inc) { + if (isfp16zero(pv[k])) { + res = k; + break; + } + } + } else if (hf = tofp16(d), d == fromfp16(hf)) { + const uint16_t *pv = p->u.array.u.fp16_ptr; + for (; k != stop; k += inc) { + if (pv[k] == hf) { + res = k; + break; + } + } + } + break; case JS_CLASS_FLOAT32_ARRAY: if (is_bigint) break; @@ -51175,6 +51236,11 @@ static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) { return (y < x) - (y > x); } +static int js_TA_cmp_float16(const void *a, const void *b, void *opaque) { + return js_cmp_doubles(fromfp16(*(const uint16_t *)a), + fromfp16(*(const uint16_t *)b)); +} + static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) { return js_cmp_doubles(*(const float *)a, *(const float *)b); } @@ -51215,6 +51281,10 @@ static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) { return JS_NewBigUint64(ctx, *(uint64_t *)a); } +static JSValue js_TA_get_float16(JSContext *ctx, const void *a) { + return js_float64(fromfp16(*(const uint16_t *)a)); +} + static JSValue js_TA_get_float32(JSContext *ctx, const void *a) { return js_float64(*(const float *)a); } @@ -51339,6 +51409,10 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValue this_val, tsc.getfun = js_TA_get_uint64; cmpfun = js_TA_cmp_uint64; break; + case JS_CLASS_FLOAT16_ARRAY: + tsc.getfun = js_TA_get_float16; + cmpfun = js_TA_cmp_float16; + break; case JS_CLASS_FLOAT32_ARRAY: tsc.getfun = js_TA_get_float32; cmpfun = js_TA_cmp_float32; @@ -51893,6 +51967,14 @@ static JSValue js_dataview_getValue(JSContext *ctx, return JS_NewBigUint64(ctx, v); } break; + case JS_CLASS_FLOAT16_ARRAY: + { + uint16_t v; + v = get_u16(ptr); + if (is_swap) + v = bswap16(v); + return js_float64(fromfp16(v)); + } case JS_CLASS_FLOAT32_ARRAY: { union { @@ -51956,7 +52038,9 @@ static JSValue js_dataview_setValue(JSContext *ctx, double d; if (JS_ToFloat64(ctx, &d, val)) return JS_EXCEPTION; - if (class_id == JS_CLASS_FLOAT32_ARRAY) { + if (class_id == JS_CLASS_FLOAT16_ARRAY) { + v = tofp16(d); + } else if (class_id == JS_CLASS_FLOAT32_ARRAY) { union { float f; uint32_t i; @@ -51985,6 +52069,7 @@ static JSValue js_dataview_setValue(JSContext *ctx, break; case JS_CLASS_INT16_ARRAY: case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_FLOAT16_ARRAY: if (is_swap) v = bswap16(v); put_u16(ptr, v); @@ -52021,6 +52106,7 @@ static const JSCFunctionListEntry js_dataview_proto_funcs[] = { JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ), JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ), JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ), + JS_CFUNC_MAGIC_DEF("getFloat16", 1, js_dataview_getValue, JS_CLASS_FLOAT16_ARRAY ), JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ), JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ), JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ), @@ -52031,6 +52117,7 @@ static const JSCFunctionListEntry js_dataview_proto_funcs[] = { JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ), JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ), JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ), + JS_CFUNC_MAGIC_DEF("setFloat16", 2, js_dataview_setValue, JS_CLASS_FLOAT16_ARRAY ), JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ), JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ), JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ), diff --git a/test262.conf b/test262.conf index 19690e6..8da70c7 100644 --- a/test262.conf +++ b/test262.conf @@ -107,7 +107,7 @@ exponentiation export-star-as-namespace-from-module FinalizationRegistry FinalizationRegistry.prototype.cleanupSome=skip -Float16Array=skip +Float16Array Float32Array Float64Array for-in-order diff --git a/test262_errors.txt b/test262_errors.txt index 000cdfd..8dceec4 100644 --- a/test262_errors.txt +++ b/test262_errors.txt @@ -76,10 +76,10 @@ test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-canoni test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-canonical-invalid-index-prototype-chain-set.js:35: strict mode: Test262Error: value should not be coerced Expected SameValue(«22», «0») to be true test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-canonical-invalid-index-reflect-set.js:35: Test262Error: value should not be coerced Expected SameValue(«32», «0») to be true test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-canonical-invalid-index-reflect-set.js:35: strict mode: Test262Error: value should not be coerced Expected SameValue(«32», «0») to be true -test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-canonical-invalid-index-prototype-chain-set.js:35: Test262Error: value should not be coerced Expected SameValue(«99», «0») to be true -test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-canonical-invalid-index-prototype-chain-set.js:35: strict mode: Test262Error: value should not be coerced Expected SameValue(«99», «0») to be true -test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-canonical-invalid-index-reflect-set.js:35: Test262Error: value should not be coerced Expected SameValue(«144», «0») to be true -test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-canonical-invalid-index-reflect-set.js:35: strict mode: Test262Error: value should not be coerced Expected SameValue(«144», «0») to be true +test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-canonical-invalid-index-prototype-chain-set.js:35: Test262Error: value should not be coerced Expected SameValue(«110», «0») to be true +test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-canonical-invalid-index-prototype-chain-set.js:35: strict mode: Test262Error: value should not be coerced Expected SameValue(«110», «0») to be true +test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-canonical-invalid-index-reflect-set.js:35: Test262Error: value should not be coerced Expected SameValue(«160», «0») to be true +test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-canonical-invalid-index-reflect-set.js:35: strict mode: Test262Error: value should not be coerced Expected SameValue(«160», «0») to be true test262/test/language/expressions/arrow-function/static-init-await-reference.js:12: unexpected error type: Test262: This statement should not be evaluated. test262/test/language/expressions/arrow-function/static-init-await-reference.js:12: strict mode: unexpected error type: Test262: This statement should not be evaluated. test262/test/language/expressions/assignment/destructuring/iterator-destructuring-property-reference-target-evaluation-order.js:42: Test262Error: Expected [source, iterator, target, target-key, target-key-tostring, iterator-step, iterator-done, set] and [source, iterator, target, target-key, iterator-step, iterator-done, target-key-tostring, set] to have the same contents. diff --git a/tests/test_bjson.js b/tests/test_bjson.js index e12919b..4139711 100644 --- a/tests/test_bjson.js +++ b/tests/test_bjson.js @@ -42,6 +42,7 @@ function isArrayLike(a) (a instanceof Int8Array) || (a instanceof Int16Array) || (a instanceof Int32Array) || + (a instanceof Float16Array) || (a instanceof Float32Array) || (a instanceof Float64Array); } @@ -192,6 +193,7 @@ function bjson_test_all() bjson_test([new Date(1234), new String("abc"), new Number(-12.1), new Boolean(true)]); bjson_test(new Int32Array([123123, 222111, -32222])); + bjson_test(new Float16Array([1024, 1024.5])); bjson_test(new Float64Array([123123, 222111.5])); /* tested with a circular reference */ diff --git a/tests/test_builtin.js b/tests/test_builtin.js index 41a4482..1e54587 100644 --- a/tests/test_builtin.js +++ b/tests/test_builtin.js @@ -551,6 +551,9 @@ function test_typed_array() a = new Uint16Array(buffer, 2); a[0] = -1; + a = new Float16Array(buffer, 8, 1); + a[0] = 1; + a = new Float32Array(buffer, 8, 1); a[0] = 1;