Add Float16Array (#1249)

This commit is contained in:
Chris Tate
2026-10-18 02:06:17 -05:00
committed by GitHub
37 changed files with 501 additions and 138 deletions
@@ -1 +1 @@
- **ArrayBuffers and typed arrays:** Fixed-length ArrayBuffers support shared typed-array and DataView storage. Resizing, transferring, Float16 arrays, and BigInt arrays are unsupported. Numeric typed arrays and Buffers preserve their kind and shared storage through `unknown`, including when nested in records and arrays. Numeric typed-array constructors can be stored and used with `new`, including constructors read from an array's `constructor` property. JavaScript numeric-array parameters and returns preserve typed-array storage. `Buffer.from`, `Buffer.allocUnsafe`, and `Buffer.concat` return views into a per-thread pool of `Buffer.poolSize` bytes (64 KiB by default) when the size is below `Buffer.poolSize >>> 1`, as in Node; a pool slab stays in memory while any of its Buffers does. Node's own startup code allocates from the first slab, so the `byteOffset` of Buffers in that slab differs from Node's. Buffers returned by other Node APIs, such as `fs.readFileSync` and `zlib`, own their `ArrayBuffer`, where Node allocates small ones from the pool.
- **ArrayBuffers and typed arrays:** Fixed-length ArrayBuffers support shared typed-array and DataView storage. Resizing, transferring, and BigInt arrays are unsupported. `Float16Array` stores IEEE 754 half-precision values, rounding each stored number to nearest-even as Node does; `Math.f16round`, `DataView.prototype.getFloat16` and `setFloat16`, and `util.types.isFloat16Array` compile. Numeric typed arrays and Buffers preserve their kind and shared storage through `unknown`, including when nested in records and arrays. Numeric typed-array constructors can be stored and used with `new`, including constructors read from an array's `constructor` property. JavaScript numeric-array parameters and returns preserve typed-array storage. `Buffer.from`, `Buffer.allocUnsafe`, and `Buffer.concat` return views into a per-thread pool of `Buffer.poolSize` bytes (64 KiB by default) when the size is below `Buffer.poolSize >>> 1`, as in Node; a pool slab stays in memory while any of its Buffers does. Node's own startup code allocates from the first slab, so the `byteOffset` of Buffers in that slab differs from Node's. Buffers returned by other Node APIs, such as `fs.readFileSync` and `zlib`, own their `ArrayBuffer`, where Node allocates small ones from the pool.
+1 -1
View File
@@ -3252,6 +3252,7 @@ declare module "util/types" {
export function isInt8Array(value: unknown): value is Int8Array;
export function isInt16Array(value: unknown): value is Int16Array;
export function isInt32Array(value: unknown): value is Int32Array;
export function isFloat16Array(value: unknown): value is Float16Array;
export function isFloat32Array(value: unknown): value is Float32Array;
export function isFloat64Array(value: unknown): value is Float64Array;
export function isMap(value: unknown): value is Map<unknown, unknown>;
@@ -3279,7 +3280,6 @@ declare module "util/types" {
export function isBoxedPrimitive(value: unknown): boolean;
export function isBigInt64Array(value: unknown): value is BigInt64Array;
export function isBigUint64Array(value: unknown): value is BigUint64Array;
export function isFloat16Array(value: unknown): boolean;
export function isCryptoKey(value: unknown): boolean;
export function isExternal(value: unknown): boolean;
}
@@ -182,7 +182,11 @@ export function emitByteNumber(
const integerType = bits <= 32 ? "i32" : "i64";
if (spec.write) {
let raw: string;
if (spec.floating) {
if (spec.floating && bits === 16) {
host.declare(`declare zeroext i16 @scr_f16_from_f64(double)`);
raw = B.tmp();
B.line(`${raw} = call zeroext i16 @scr_f16_from_f64(double ${value!.name})`);
} else if (spec.floating) {
let stored = value!.name;
if (bits === 32) {
stored = B.tmp();
@@ -228,7 +232,10 @@ export function emitByteNumber(
const raw = endianBits(host, loaded, spec.width, little);
fastResult = B.tmp();
if (spec.floating) {
if (bits === 32) {
if (bits === 16) {
host.declare(`declare double @scr_f16_to_f64(i16 zeroext)`);
B.line(`${fastResult} = call double @scr_f16_to_f64(i16 zeroext ${raw})`);
} else if (bits === 32) {
const float = B.tmp();
B.line(`${float} = bitcast i32 ${raw} to float`);
B.line(`${fastResult} = fpext float ${float} to double`);
@@ -159,6 +159,7 @@ export const BYTES_ELEM_NUM: Record<IrBytesElem, number> = {
u16: 6,
i16: 7,
u8c: 8,
f16: 9,
};
/** The payload marker of an ABSENT record field slot (IR fieldAbsent): the
@@ -44,6 +44,7 @@ const DV_GET_KIND: Record<string, number> = {
dvGetFloat64: 7,
dvGetBigUint64Number: 8,
dvGetBigInt64Number: 9,
dvGetFloat16: 10,
};
const DV_SET_KIND: Record<string, number> = {
@@ -55,6 +56,7 @@ const DV_SET_KIND: Record<string, number> = {
dvSetInt32: 5,
dvSetFloat32: 6,
dvSetFloat64: 7,
dvSetFloat16: 10,
};
export function emitIntegerLoopIndex(host: LlvmEmitterContext, expr: IrExpr): string | null {
@@ -273,7 +275,7 @@ function emitSharedOrLocalLoad(
idx: string,
): LlValue {
const B = host.B;
const floating = elem === "f32" || elem === "f64";
const floating = elem === "f16" || elem === "f32" || elem === "f64";
const fn = floating ? "scr_bytes_shared_get_f64" : "scr_bytes_shared_get_int";
const ret = floating ? "double" : "i32";
host.declare(`declare ${ret} @${fn}(ptr, ${host.sizeType})`);
@@ -314,6 +316,16 @@ function emitBytesLoadAt(
const B = host.B;
const data = host.emitBytesData(receiver);
const p = B.tmp();
if (elem === "f16") {
// Half-precision bits widen exactly; the runtime converts them.
host.declare(`declare double @scr_f16_to_f64(i16 zeroext)`);
const raw = B.tmp();
const out = B.tmp();
B.line(`${p} = getelementptr inbounds i16, ptr ${data}, ${host.sizeType} ${idx}`);
B.line(`${raw} = load i16, ptr ${p}, align 1`);
B.line(`${out} = call double @scr_f16_to_f64(i16 zeroext ${raw})`);
return { name: out, type: F64 };
}
if (BYTES_ELEMENT_SIZE[elem] < 4) {
const bits = BYTES_ELEMENT_SIZE[elem] * 8;
const signed = elem === "i8" || elem === "i16";
@@ -472,10 +484,19 @@ export function emitBytesSet(
B.line(`${stored} = fptoui double ${clamped} to i32`);
} else
stored =
elem === "f32" || elem === "f64" ? null : host.emitToUint32(value.name, expr, value.uint32);
elem === "f16" || elem === "f32" || elem === "f64"
? null
: host.emitToUint32(value.name, expr, value.uint32);
const data = host.emitBytesData(receiver);
const p = B.tmp();
if (BYTES_ELEMENT_SIZE[elem] < 4) {
if (elem === "f16") {
// One round-to-nearest-even step from the double, in the runtime.
host.declare(`declare zeroext i16 @scr_f16_from_f64(double)`);
const narrowed = B.tmp();
B.line(`${narrowed} = call zeroext i16 @scr_f16_from_f64(double ${value.name})`);
B.line(`${p} = getelementptr inbounds i16, ptr ${data}, ${host.sizeType} ${idx}`);
B.line(`store i16 ${narrowed}, ptr ${p}, align 1`);
} else if (BYTES_ELEMENT_SIZE[elem] < 4) {
const bits = BYTES_ELEMENT_SIZE[elem] * 8;
const byte = B.tmp();
B.line(`${byte} = trunc i32 ${stored!} to i${bits}`);
@@ -882,6 +903,7 @@ export function emitBytesIntrinsic(
case "dvGetInt16":
case "dvGetUint32":
case "dvGetInt32":
case "dvGetFloat16":
case "dvGetFloat32":
case "dvGetFloat64":
case "dvGetBigUint64Number":
@@ -912,6 +934,7 @@ export function emitBytesIntrinsic(
case "dvSetInt16":
case "dvSetUint32":
case "dvSetInt32":
case "dvSetFloat16":
case "dvSetFloat32":
case "dvSetFloat64": {
// DataView setters: [offset, value, littleEndian?] — void; throw
@@ -1039,6 +1039,16 @@ export function emitPrimitiveLibCall(host: LlvmEmitterContext, e: LibCallExpr):
B.line(`${t} = sitofp i32 ${product} to double`);
return { name: t, type: e.type, uint32: product };
}
if (e.fn === "math.f16round") {
const v = host.emitExpr(e.args[0]!);
host.declare(`declare zeroext i16 @scr_f16_from_f64(double)`);
host.declare(`declare double @scr_f16_to_f64(i16 zeroext)`);
const narrowed = B.tmp();
const t = B.tmp();
B.line(`${narrowed} = call zeroext i16 @scr_f16_from_f64(double ${v.name})`);
B.line(`${t} = call double @scr_f16_to_f64(i16 zeroext ${narrowed})`);
return { name: t, type: e.type };
}
if (e.fn === "math.fround") {
const v = host.emitExpr(e.args[0]!);
const narrowed = B.tmp();
@@ -65,6 +65,7 @@ export const LIB_FN_SYMS: Record<string, string> = {
"arrayBuffer.viewU8": "scr_array_buffer_view_u8",
"arrayBuffer.viewU32": "scr_array_buffer_view_u32",
"arrayBuffer.viewI32": "scr_array_buffer_view_i32",
"arrayBuffer.viewF16": "scr_array_buffer_view_f16",
"arrayBuffer.viewF32": "scr_array_buffer_view_f32",
"arrayBuffer.viewF64": "scr_array_buffer_view_f64",
"arrayBuffer.viewDV": "scr_array_buffer_view_dv",
@@ -108,6 +108,7 @@ const PURE_MATH = new Set<string>([
"math.exp",
"math.expm1",
"math.floor",
"math.f16round",
"math.fround",
"math.imul",
"math.log",
@@ -151,7 +151,7 @@ function lowerBytesSortCall(
/* ── typed arrays / Buffer ─────────────────────────────────────────────── */
/** The typed-array constructors with a runtime representation, by lib
* interface name. Other flavors (Float16Array, BigInt64Array, ...) fall through
* interface name. Other flavors (BigInt64Array, BigUint64Array) fall through
* to the generic stdlib-constructor fence. DataView is handled separately. */
const BYTES_CTORS: Record<string, IrBytesElem | undefined> = {
Uint8Array: "u8",
@@ -161,6 +161,7 @@ const BYTES_CTORS: Record<string, IrBytesElem | undefined> = {
Int16Array: "i16",
Uint32Array: "u32",
Int32Array: "i32",
Float16Array: "f16",
Float32Array: "f32",
Float64Array: "f64",
};
@@ -397,6 +398,7 @@ function lowerArrayBufferView(
u8: "arrayBuffer.viewU8",
u32: "arrayBuffer.viewU32",
i32: "arrayBuffer.viewI32",
f16: "arrayBuffer.viewF16",
f32: "arrayBuffer.viewF32",
f64: "arrayBuffer.viewF64",
dv: "arrayBuffer.viewDV",
@@ -1220,6 +1222,7 @@ const DV_GETTERS: Record<string, { method: IrBytesIntrinsicMethod; le: boolean }
getInt16: { method: "dvGetInt16", le: true },
getUint32: { method: "dvGetUint32", le: true },
getInt32: { method: "dvGetInt32", le: true },
getFloat16: { method: "dvGetFloat16", le: true },
getFloat32: { method: "dvGetFloat32", le: true },
getFloat64: { method: "dvGetFloat64", le: true },
};
@@ -1231,6 +1234,7 @@ const DV_SETTERS: Record<string, { method: IrBytesIntrinsicMethod; le: boolean }
setInt16: { method: "dvSetInt16", le: true },
setUint32: { method: "dvSetUint32", le: true },
setInt32: { method: "dvSetInt32", le: true },
setFloat16: { method: "dvSetFloat16", le: true },
setFloat32: { method: "dvSetFloat32", le: true },
setFloat64: { method: "dvSetFloat64", le: true },
};
@@ -2364,6 +2364,7 @@ const BYTES_BRANDS: ReadonlySet<string> = new Set([
"Int8Array",
"Int16Array",
"Int32Array",
"Float16Array",
"Float32Array",
"Float64Array",
"BigInt64Array",
@@ -191,6 +191,7 @@ const TYPED_ARRAYS = new Set([
"Uint16Array",
"Int32Array",
"Uint32Array",
"Float16Array",
"Float32Array",
"Float64Array",
]);
@@ -834,6 +834,7 @@ const INERT_CONSTRUCTORS = new Set([
"Int16Array",
"Uint32Array",
"Int32Array",
"Float16Array",
"Float32Array",
"Float64Array",
"BigInt64Array",
@@ -664,6 +664,7 @@ export const STDLIB_SURFACE = {
cosh: ISL_N1,
exp: ISL_N1,
expm1: ISL_N1,
f16round: ISL_N1,
fround: ISL_N1,
hypot: ISL_N2,
imul: ISL_N2,
@@ -751,6 +752,7 @@ export const STATIC_MATH_FNS: Record<string, { fn: IrLibFn; arity: number } | un
sign: { fn: "math.sign", arity: 1 },
exp: { fn: "math.exp", arity: 1 },
expm1: { fn: "math.expm1", arity: 1 },
f16round: { fn: "math.f16round", arity: 1 },
fround: { fn: "math.fround", arity: 1 },
sqrt: { fn: "math.sqrt", arity: 1 },
log: { fn: "math.log", arity: 1 },
@@ -2521,6 +2521,7 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null {
// ToInt32-wrap) — the Atomics.wait sleep idiom constructs one over a
// SharedArrayBuffer, and the i32 semantics hold for every other use.
if (isStdlibInterface("Int32Array")) return bytesOf("i32");
if (isStdlibInterface("Float16Array")) return bytesOf("f16");
if (isStdlibInterface("Float32Array")) return bytesOf("f32");
if (isStdlibInterface("Float64Array")) return bytesOf("f64");
// DataView: the ONE view kind — a u8 bytes value whose runtime
+1
View File
@@ -219,6 +219,7 @@ const BORROW_SAFE_MATH = new Set<IrLibFn>([
"math.sign",
"math.exp",
"math.expm1",
"math.f16round",
"math.fround",
"math.log",
"math.log1p",
@@ -75,6 +75,7 @@ export const MAY_THROW_LIB_FNS: ReadonlySet<IrLibFn> = new Set([
"arrayBuffer.viewU8",
"arrayBuffer.viewU32",
"arrayBuffer.viewI32",
"arrayBuffer.viewF16",
"arrayBuffer.viewF32",
"arrayBuffer.viewF64",
"arrayBuffer.viewDV",
@@ -648,6 +648,7 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
"math.sign": { argTypes: [F64], result: F64 },
"math.exp": { argTypes: [F64], result: F64 },
"math.expm1": { argTypes: [F64], result: F64 },
"math.f16round": { argTypes: [F64], result: F64 },
"math.fround": { argTypes: [F64], result: F64 },
"math.sqrt": { argTypes: [F64], result: F64 },
"math.log": { argTypes: [F64], result: F64 },
@@ -3163,6 +3164,7 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
"arrayBuffer.viewU8": { argTypes: [DYN, DYN, DYN], result: BYTES_U8 },
"arrayBuffer.viewU32": { argTypes: [DYN, DYN, DYN], result: bytesOf("u32") },
"arrayBuffer.viewI32": { argTypes: [DYN, DYN, DYN], result: bytesOf("i32") },
"arrayBuffer.viewF16": { argTypes: [DYN, DYN, DYN], result: bytesOf("f16") },
"arrayBuffer.viewF32": { argTypes: [DYN, DYN, DYN], result: bytesOf("f32") },
"arrayBuffer.viewF64": { argTypes: [DYN, DYN, DYN], result: bytesOf("f64") },
"arrayBuffer.viewDV": { argTypes: [DYN, DYN, DYN], result: BYTES_U8 },
+3
View File
@@ -25,6 +25,7 @@ const FORMATS: Readonly<Record<string, ByteNumberFormat | undefined>> = {
i16: { width: 2, signed: true, floating: false },
u32: { width: 4, signed: false, floating: false },
i32: { width: 4, signed: true, floating: false },
f16: { width: 2, signed: false, floating: true },
f32: { width: 4, signed: false, floating: true },
f64: { width: 8, signed: false, floating: true },
u64: { width: 8, signed: false, floating: false },
@@ -38,6 +39,7 @@ const VIEW_FORMATS: Readonly<Record<string, string | undefined>> = {
dvGetInt16: "i16",
dvGetUint32: "u32",
dvGetInt32: "i32",
dvGetFloat16: "f16",
dvGetFloat32: "f32",
dvGetFloat64: "f64",
dvGetBigUint64Number: "u64",
@@ -48,6 +50,7 @@ const VIEW_FORMATS: Readonly<Record<string, string | undefined>> = {
dvSetInt16: "i16",
dvSetUint32: "u32",
dvSetInt32: "i32",
dvSetFloat16: "f16",
dvSetFloat32: "f32",
dvSetFloat64: "f64",
};
+1
View File
@@ -336,6 +336,7 @@ const BOUNDARY_KINDS = new Set<IrExpr["kind"]>([
* property, except literal-keyed `dyn.keySet`/`dyn.keyDelete`. */
const DYN_READ_ONLY = new Set<string>([
"arrayBuffer.new",
"arrayBuffer.viewF16",
"arrayBuffer.viewF32",
"arrayBuffer.viewF64",
"arrayBuffer.viewI16",
+20 -4
View File
@@ -26,9 +26,19 @@ export interface SrcLoc {
/* ── types ─────────────────────────────────────────────────────────────── */
/** Numeric typed-array element kinds with a native representation. Float16
* and BigInt typed arrays remain frontend-fenced. */
export type IrBytesElem = "u8" | "u8c" | "i8" | "u16" | "i16" | "u32" | "i32" | "f32" | "f64";
/** Numeric typed-array element kinds with a native representation. BigInt
* typed arrays remain frontend-fenced. */
export type IrBytesElem =
| "u8"
| "u8c"
| "i8"
| "u16"
| "i16"
| "u32"
| "i32"
| "f16"
| "f32"
| "f64";
export const BYTES_ELEMENT_SIZE: Record<IrBytesElem, number> = {
u8: 1,
@@ -38,13 +48,14 @@ export const BYTES_ELEMENT_SIZE: Record<IrBytesElem, number> = {
i16: 2,
u32: 4,
i32: 4,
f16: 2,
f32: 4,
f64: 8,
};
/** Integer element kinds: a valid element read is never NaN. */
export function isIntegerBytesElem(elem: IrBytesElem): boolean {
return elem !== "f32" && elem !== "f64";
return elem !== "f16" && elem !== "f32" && elem !== "f64";
}
export const BYTES_ELEMENT_NAME: Record<IrBytesElem, string> = {
@@ -55,6 +66,7 @@ export const BYTES_ELEMENT_NAME: Record<IrBytesElem, string> = {
i16: "Int16Array",
u32: "Uint32Array",
i32: "Int32Array",
f16: "Float16Array",
f32: "Float32Array",
f64: "Float64Array",
};
@@ -2242,6 +2254,7 @@ export type IrBytesIntrinsicMethod =
| "dvGetInt16"
| "dvGetUint32"
| "dvGetInt32"
| "dvGetFloat16"
| "dvGetFloat32"
| "dvGetFloat64"
| "dvGetBigUint64Number"
@@ -2252,6 +2265,7 @@ export type IrBytesIntrinsicMethod =
| "dvSetInt16"
| "dvSetUint32"
| "dvSetInt32"
| "dvSetFloat16"
| "dvSetFloat32"
| "dvSetFloat64";
@@ -2283,6 +2297,7 @@ export const MAY_THROW_BYTES_METHODS: ReadonlySet<IrBytesIntrinsicMethod> = new
"dvGetInt16",
"dvGetUint32",
"dvGetInt32",
"dvGetFloat16",
"dvGetFloat32",
"dvGetFloat64",
"dvGetBigUint64Number",
@@ -2293,6 +2308,7 @@ export const MAY_THROW_BYTES_METHODS: ReadonlySet<IrBytesIntrinsicMethod> = new
"dvSetInt16",
"dvSetUint32",
"dvSetInt32",
"dvSetFloat16",
"dvSetFloat32",
"dvSetFloat64",
]);
+7
View File
@@ -4602,6 +4602,13 @@
"status": "static",
"note": "compiles statically at arity 1; other declared call shapes are refused with SC2012"
},
{
"id": "stdlib.math.f16round",
"kind": "stdlib",
"name": "Math.f16round",
"status": "static",
"note": "compiles statically at arity 1; other declared call shapes are refused with SC2012"
},
{
"id": "stdlib.math.floor",
"kind": "stdlib",
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/5900-float16-array.ts"
],
"diags": []
}
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/5901-float16-array-js.js"
],
"diags": []
}
+36 -1
View File
@@ -3111,6 +3111,11 @@ static double scr_bytes_sort_load(const ScrBytes *b, size_t i) {
memcpy(&v, at, sizeof v);
return v;
}
case SCR_BYTES_F16: {
uint16_t v;
memcpy(&v, at, sizeof v);
return scr_f16_to_f64(v);
}
case SCR_BYTES_F32: {
float v;
memcpy(&v, at, sizeof v);
@@ -3151,6 +3156,11 @@ static void scr_bytes_sort_store(ScrBytes *b, size_t i, double value) {
memcpy(at, &v, sizeof v);
break;
}
case SCR_BYTES_F16: {
uint16_t v = scr_f16_from_f64(value);
memcpy(at, &v, sizeof v);
break;
}
case SCR_BYTES_F32: {
float v = (float)value;
memcpy(at, &v, sizeof v);
@@ -3263,6 +3273,18 @@ static int scr_bytes_f32_key(const uint8_t *at, int32_t order, uint32_t *key) {
return 0;
}
/* Float16 keys use the low 16 bits of a 32-bit key; a descending order
* complements all 32, which keeps the order of keys. */
static int scr_bytes_f16_key(const uint8_t *at, int32_t order, uint32_t *key) {
uint16_t bits;
memcpy(&bits, at, sizeof bits);
if ((bits & 0x7fff) > 0x7c00) return order ? 2 : 1;
if (order && bits == 0x8000) return 2;
uint32_t k = (bits & 0x8000) ? (uint16_t)~bits : (uint32_t)bits | 0x8000;
*key = k ^ (order < 0 ? ~UINT32_C(0) : 0);
return 0;
}
static int scr_bytes_f64_key(const uint8_t *at, int32_t order, uint64_t *key) {
double v;
uint64_t bits;
@@ -3323,6 +3345,9 @@ static bool scr_bytes_sort_refused(const ScrBytes *b, int32_t order) {
if (b->elem == SCR_BYTES_F32) {
uint32_t key;
if (scr_bytes_f32_key(data + i * 4, order, &key)) return true;
} else if (b->elem == SCR_BYTES_F16) {
uint32_t key;
if (scr_bytes_f16_key(data + i * 2, order, &key)) return true;
} else if (b->elem == SCR_BYTES_F64) {
uint64_t key;
if (scr_bytes_f64_key(data + i * 8, order, &key)) return true;
@@ -3353,7 +3378,7 @@ bool scr_bytes_sort_numeric(ScrBytes *b, int32_t order) {
size_t width = scr_bytes_elem_size(b->elem);
uint8_t *data = b->data;
/* Integer elements have no NaN or -0: every order is a value order. */
if (width == 1 || (width == 2 && n >= SCR_BYTES_COUNT_SORT)) {
if (width == 1 || (width == 2 && b->elem != SCR_BYTES_F16 && n >= SCR_BYTES_COUNT_SORT)) {
scr_bytes_count_sort(b, order < 0);
return true;
}
@@ -3392,13 +3417,23 @@ bool scr_bytes_sort_numeric(ScrBytes *b, int32_t order) {
uint32_t bits_ = (k_ & SCR_SORT_SIGN32) ? k_ & ~SCR_SORT_SIGN32 : ~k_; \
memcpy(data + (i) * 4, &bits_, 4); \
} while (0)
#define SCR_BYTES_READ32_F16(i, key) scr_bytes_f16_key(data + (i) * 2, order, (key))
#define SCR_BYTES_WRITE32_F16(i, key) \
do { \
uint16_t k_ = (uint16_t)((key) ^ flip32); \
uint16_t bits_ = (k_ & 0x8000) ? (uint16_t)(k_ & 0x7fff) : (uint16_t)~k_; \
memcpy(data + (i) * 2, &bits_, 2); \
} while (0)
if (b->elem == SCR_BYTES_F32) SCR_BYTES_KEYS32(F32);
else if (b->elem == SCR_BYTES_F16) SCR_BYTES_KEYS32(F16);
else SCR_BYTES_KEYS32(INT);
#undef SCR_BYTES_KEYS32
#undef SCR_BYTES_READ32_INT
#undef SCR_BYTES_WRITE32_INT
#undef SCR_BYTES_READ32_F32
#undef SCR_BYTES_WRITE32_F32
#undef SCR_BYTES_READ32_F16
#undef SCR_BYTES_WRITE32_F16
free(keys);
return true;
}
+2 -1
View File
@@ -791,7 +791,7 @@ static bool scr_assert_loose_error(const ScrDyn *a, const ScrDyn *b) {
}
static bool scr_assert_loose_float_elem(ScrBytesElem elem) {
return elem == SCR_BYTES_F32 || elem == SCR_BYTES_F64;
return elem == SCR_BYTES_F16 || elem == SCR_BYTES_F32 || elem == SCR_BYTES_F64;
}
/* Typed arrays and Buffers: the same tag (a Buffer is a Uint8Array here),
@@ -1305,6 +1305,7 @@ static const char *scr_assert_cf_bytes_class(ScrBytesElem elem) {
switch (elem) {
case SCR_BYTES_U8: return "Uint8Array";
case SCR_BYTES_U32: return "Uint32Array";
case SCR_BYTES_F16: return "Float16Array";
case SCR_BYTES_F32: return "Float32Array";
case SCR_BYTES_I32: return "Int32Array";
case SCR_BYTES_F64: return "Float64Array";
+78 -1
View File
@@ -87,10 +87,60 @@ static void scr_bytes_oom(void) {
scr_trap("scriptc: out of memory\n");
}
uint16_t scr_f16_from_f64(double value) {
uint64_t bits;
memcpy(&bits, &value, sizeof bits);
uint16_t sign = (uint16_t)((bits >> 48) & 0x8000);
uint64_t magnitude = bits & UINT64_C(0x7fffffffffffffff);
if (magnitude >= UINT64_C(0x7ff0000000000000)) {
if (magnitude == UINT64_C(0x7ff0000000000000)) return sign | 0x7c00;
return sign | 0x7e00 | (uint16_t)((magnitude >> 42) & 0x3ff);
}
int exponent = (int)(magnitude >> 52) - 1023;
if (exponent > 15) return sign | 0x7c00;
uint64_t fraction = magnitude & ((UINT64_C(1) << 52) - 1);
uint64_t significand = exponent == -1023 ? fraction : fraction | (UINT64_C(1) << 52);
if (exponent == -1023) exponent = -1022;
int biased = exponent + 15;
/* Normal results keep 10 of the 52 fraction bits; subnormal results
* shift further, by how far the exponent is below the normal range. */
int shift = biased >= 1 ? 42 : 42 + (1 - biased);
if (shift >= 54) return sign;
uint64_t q = significand >> shift;
uint64_t rest = significand & ((UINT64_C(1) << shift) - 1);
uint64_t half = UINT64_C(1) << (shift - 1);
if (rest > half || (rest == half && (q & 1))) q++;
if (biased < 1) return sign | (uint16_t)q; /* a carry into 0x400 is the smallest normal */
if (q == 2048) {
q = 1024;
biased++;
}
if (biased >= 31) return sign | 0x7c00;
return sign | (uint16_t)(biased << 10) | (uint16_t)(q & 0x3ff);
}
double scr_f16_to_f64(uint16_t h) {
uint64_t sign = (uint64_t)(h >> 15) << 63;
unsigned exponent = (h >> 10) & 0x1f, fraction = h & 0x3ff;
uint64_t bits;
if (exponent == 0) {
double v = (double)fraction * 5.9604644775390625e-8; /* fraction * 2^-24, exact */
memcpy(&bits, &v, sizeof bits);
bits |= sign;
} else if (exponent == 31) {
bits = sign | UINT64_C(0x7ff0000000000000) | ((uint64_t)fraction << 42);
} else {
bits = sign | ((uint64_t)(exponent - 15 + 1023) << 52) | ((uint64_t)fraction << 42);
}
double out;
memcpy(&out, &bits, sizeof out);
return out;
}
size_t scr_bytes_elem_size(ScrBytesElem elem) {
switch (elem) {
case SCR_BYTES_U8: case SCR_BYTES_U8C: case SCR_BYTES_I8: return 1;
case SCR_BYTES_U16: case SCR_BYTES_I16: return 2;
case SCR_BYTES_U16: case SCR_BYTES_I16: case SCR_BYTES_F16: return 2;
case SCR_BYTES_U32: case SCR_BYTES_I32: case SCR_BYTES_F32: return 4;
case SCR_BYTES_F64: return 8;
}
@@ -106,6 +156,7 @@ const char *scr_bytes_elem_name(ScrBytesElem elem) {
case SCR_BYTES_I16: return "Int16Array";
case SCR_BYTES_U32: return "Uint32Array";
case SCR_BYTES_I32: return "Int32Array";
case SCR_BYTES_F16: return "Float16Array";
case SCR_BYTES_F32: return "Float32Array";
case SCR_BYTES_F64: return "Float64Array";
}
@@ -329,6 +380,11 @@ static double scr_bytes_decode(ScrBytesElem elem, const uint8_t *p) {
memcpy(&v, p, 4);
return (double)v;
}
case SCR_BYTES_F16: {
uint16_t v;
memcpy(&v, p, 2);
return scr_f16_to_f64(v);
}
case SCR_BYTES_F32: {
float v;
memcpy(&v, p, 4);
@@ -368,6 +424,11 @@ static void scr_bytes_encode(ScrBytesElem elem, uint8_t *p, double v) {
memcpy(p, &u, 4);
break;
}
case SCR_BYTES_F16: {
uint16_t h = scr_f16_from_f64(v);
memcpy(p, &h, 2);
break;
}
case SCR_BYTES_F32: {
float f = (float)v; /* round-to-nearest-even, exactly Float32Array */
memcpy(p, &f, 4);
@@ -511,6 +572,13 @@ static void scr_bytes_read_numbers(double *out, const uint8_t *src, ScrBytesElem
SCR_READ_NUMBERS(SCR_BYTES_I32, int32_t);
SCR_READ_NUMBERS(SCR_BYTES_F32, float);
SCR_READ_NUMBERS(SCR_BYTES_F64, double);
case SCR_BYTES_F16:
for (size_t i = 0; i < count; i++) {
uint16_t value;
memcpy(&value, src + i * sizeof value, sizeof value);
out[i] = scr_f16_to_f64(value);
}
return;
}
#undef SCR_READ_NUMBERS
}
@@ -535,6 +603,9 @@ static void scr_bytes_write_numbers(uint8_t *dst, ScrBytesElem elem, const doubl
case SCR_BYTES_U32: case SCR_BYTES_I32: {
SCR_WRITE_NUMBERS(uint32_t, scr_numeric_to_u32(src[i]));
}
case SCR_BYTES_F16: {
SCR_WRITE_NUMBERS(uint16_t, scr_f16_from_f64(src[i]));
}
case SCR_BYTES_F32: {
SCR_WRITE_NUMBERS(float, (float)src[i]);
}
@@ -936,6 +1007,7 @@ static size_t scr_dataview_get_size(ScrDataViewGet kind) {
return 1;
case SCR_DV_U16:
case SCR_DV_I16:
case SCR_DV_F16:
return 2;
case SCR_DV_U32:
case SCR_DV_I32:
@@ -974,6 +1046,8 @@ double scr_dataview_get(const ScrBytes *b, double byte_off, ScrDataViewGet kind,
return (double)(int16_t)u;
case SCR_DV_I32:
return (double)(int32_t)u;
case SCR_DV_F16:
return scr_f16_to_f64((uint16_t)u);
case SCR_DV_F32: {
uint32_t bits = (uint32_t)u;
float f;
@@ -1018,6 +1092,9 @@ void scr_dataview_set(ScrBytes *b, double byte_off, double value, ScrDataViewGet
case SCR_DV_I32:
u = (uint64_t)scr_bytes_to_u32(value);
break;
case SCR_DV_F16:
u = scr_f16_from_f64(value);
break;
case SCR_DV_F32: {
float f = (float)value;
uint32_t bits;
+13 -2
View File
@@ -20,7 +20,8 @@
* Elements are scalars only and the backing edge is acyclic by
* construction (owners point at nothing): never part of a cycle, no
* trace. Element reads widen to double; writes coerce JS-exactly (ToUint8
* / ToUint32 modular truncation, double→float rounding for f32).
* / ToUint32 modular truncation, double→float rounding for f32, one
* double→binary16 rounding for f16).
* Out-of-bounds typed ELEMENT reads trap like arrays; invalid writes are
* ignored as in JavaScript. The Node-shaped operations
* (construction lengths, set(), the read/write numeric families, the
@@ -37,7 +38,16 @@ typedef enum ScrBytesElem {
SCR_BYTES_U16, /* Uint16Array */
SCR_BYTES_I16, /* Int16Array */
SCR_BYTES_U8C, /* Uint8ClampedArray */
SCR_BYTES_F16, /* Float16Array (IEEE 754 binary16 storage) */
} ScrBytesElem;
#define SCR_BYTES_ELEM_COUNT 10
/* Float16Array's element conversions: one round-to-nearest-even step from
* the double (Float16Array stores, Math.f16round and DataView.setFloat16
* all round this way), and the exact widening back. NaN keeps its sign and
* top payload bits with the quiet bit set. */
uint16_t scr_f16_from_f64(double value);
double scr_f16_to_f64(uint16_t bits);
typedef struct ScrSharedBytes ScrSharedBytes;
@@ -149,6 +159,7 @@ ScrBytes *scr_array_buffer_view_i16(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_u8(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_u32(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_i32(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_f16(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_f32(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_f64(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_dv(ScrDyn *, ScrDyn *, ScrDyn *);
@@ -206,7 +217,7 @@ ScrBytes *scr_dataview_new(ScrBytes *src, double byte_off, bool has_len, double
* with the exception pending). */
typedef enum ScrDataViewGet {
SCR_DV_U8, SCR_DV_I8, SCR_DV_U16, SCR_DV_I16, SCR_DV_U32, SCR_DV_I32,
SCR_DV_F32, SCR_DV_F64, SCR_DV_BIGU64, SCR_DV_BIGI64,
SCR_DV_F32, SCR_DV_F64, SCR_DV_BIGU64, SCR_DV_BIGI64, SCR_DV_F16,
} ScrDataViewGet;
double scr_dataview_get(const ScrBytes *b, double byte_off, ScrDataViewGet kind, bool le);
+3 -3
View File
@@ -1520,7 +1520,7 @@ static ScrDyn *scr_dyn_invoke_impl(
if (recv->kind == SCR_DYN_BYTES) {
ScrBytes *bytes = recv->v.bytes;
if (bytes->is_data_view) {
static const char *const kinds[] = { "Uint8", "Int8", "Uint16", "Int16", "Uint32", "Int32", "Float32", "Float64", "BigUint64", "BigInt64" };
static const char *const kinds[] = { "Uint8", "Int8", "Uint16", "Int16", "Uint32", "Int32", "Float32", "Float64", "BigUint64", "BigInt64", "Float16" };
const bool get = !strncmp(method, "get", 3);
const bool set = !strncmp(method, "set", 3);
for (size_t kind = 0; (get || set) && kind < sizeof kinds / sizeof kinds[0]; kind++) {
@@ -1537,7 +1537,7 @@ static ScrDyn *scr_dyn_invoke_impl(
}
const bool le = argc > (get ? 1 : 2) && scr_dyn_truthy(args[get ? 1 : 2]);
if (get) {
if (kind >= SCR_DV_BIGU64) {
if (kind == SCR_DV_BIGU64 || kind == SCR_DV_BIGI64) {
ScrBigInt *value = scr_bigint_dataview_get(bytes, offset, kind == SCR_DV_BIGI64, le);
if (scr_exc_pending()) return NULL;
ScrDyn *result = scr_dyn_new_bigint(value);
@@ -1547,7 +1547,7 @@ static ScrDyn *scr_dyn_invoke_impl(
double value = scr_dataview_get(bytes, offset, (ScrDataViewGet)kind, le);
return scr_exc_pending() ? NULL : scr_dyn_new_num(value);
}
if (kind >= SCR_DV_BIGU64) {
if (kind == SCR_DV_BIGU64 || kind == SCR_DV_BIGI64) {
ScrBigInt *value = scr_dyn_bigint_coerce(argc > 1 ? args[1] : scr_dyn_undefined());
if (scr_exc_pending()) return NULL;
scr_bigint_dataview_set(bytes, offset, value, le);
+13 -13
View File
@@ -3146,6 +3146,7 @@ bool scr_dyn_util_type_is(const ScrDyn *value, const ScrStr *probe) {
if (PROBE("isInt8Array")) return scr_dyn_typed_array_is(value, SCR_BYTES_I8);
if (PROBE("isInt16Array")) return scr_dyn_typed_array_is(value, SCR_BYTES_I16);
if (PROBE("isInt32Array")) return scr_dyn_typed_array_is(value, SCR_BYTES_I32);
if (PROBE("isFloat16Array")) return scr_dyn_typed_array_is(value, SCR_BYTES_F16);
if (PROBE("isFloat32Array")) return scr_dyn_typed_array_is(value, SCR_BYTES_F32);
if (PROBE("isFloat64Array")) return scr_dyn_typed_array_is(value, SCR_BYTES_F64);
if (PROBE("isMap")) return value->kind == SCR_DYN_HANDLE && value->v.handle.tag == SCR_DYNH_MAP;
@@ -3162,13 +3163,11 @@ bool scr_dyn_util_type_is(const ScrDyn *value, const ScrStr *probe) {
if (PROBE("isMapIterator")) return scr_native_iterator_over(value, 2);
if (PROBE("isSetIterator")) return scr_native_iterator_over(value, 3);
if (PROBE("isKeyObject")) return value->kind == SCR_DYN_HANDLE && value->v.handle.tag == SCR_DYNH_KEY_OBJECT;
/* Boxed primitives, BigInt64Array, BigUint64Array, Float16Array, CryptoKey
* and external values cannot be created here, so no value has these
* brands. */
/* Boxed primitives, BigInt64Array, BigUint64Array, CryptoKey and external
* values cannot be created here, so no value has these brands. */
if (PROBE("isNumberObject") || PROBE("isStringObject") || PROBE("isBooleanObject") ||
PROBE("isBigIntObject") || PROBE("isSymbolObject") || PROBE("isBoxedPrimitive") ||
PROBE("isBigInt64Array") || PROBE("isBigUint64Array") || PROBE("isFloat16Array") ||
PROBE("isCryptoKey") || PROBE("isExternal"))
PROBE("isBigInt64Array") || PROBE("isBigUint64Array") || PROBE("isCryptoKey") || PROBE("isExternal"))
return false;
if (PROBE("isNativeError")) {
ScrError *error = scr_errdyn_err_of(value);
@@ -3184,7 +3183,7 @@ bool scr_dyn_util_type_is(const ScrDyn *value, const ScrStr *probe) {
* anonymous with length zero. Keep their native closure identity intact. */
ScrDyn *scr_dyn_util_type_value(const ScrDyn *value, const ScrStr *probe) {
static const char *const named[] = { "isDataView", "isTypedArray", "isUint8Array", "isUint8ClampedArray", "isUint16Array", "isUint32Array",
"isInt8Array", "isInt16Array", "isInt32Array", "isFloat32Array", "isFloat64Array" };
"isInt8Array", "isInt16Array", "isInt32Array", "isFloat16Array", "isFloat32Array", "isFloat64Array" };
const char *name = "";
if (probe->len == 17 && memcmp(probe->data, "isArrayBufferView", 17) == 0) name = "isView";
for (size_t i = 0; i < sizeof named / sizeof named[0]; i++)
@@ -3203,16 +3202,16 @@ ScrBytes *scr_dyn_bytes_unbox(const ScrDyn *d) {
/* Numeric typed-array constructor values share native function identity.
* The cache owns one reference until runtime cleanup; arbitrary strings or
* functions cannot impersonate one of these constructors. */
static SCR_TL ScrDyn *scr_bytes_ctors[9];
static SCR_TL ScrDyn *scr_bytes_ctors[SCR_BYTES_ELEM_COUNT];
static void scr_bytes_ctors_cleanup(void) {
for (size_t i = 0; i < 9; i++) {
for (size_t i = 0; i < SCR_BYTES_ELEM_COUNT; i++) {
scr_dyn_release(scr_bytes_ctors[i]);
scr_bytes_ctors[i] = NULL;
}
}
static ScrDyn *scr_bytes_ctor_call(ScrClosure *closure, ScrDyn *const *args, size_t argc) {
(void)args; (void)argc;
for (size_t i = 0; i < 9; i++) {
for (size_t i = 0; i < SCR_BYTES_ELEM_COUNT; i++) {
if (!scr_bytes_ctors[i] || scr_bytes_ctors[i]->v.fn.clo != closure) continue;
ScrJsonBuf message;
scr_jb_init(&message);
@@ -3228,7 +3227,7 @@ static ScrDyn *scr_dyn_fn_properties(const ScrDyn *function);
static ScrDyn *scr_bytes_constructor_for(ScrBytesElem elem) {
if (!scr_bytes_ctors[elem]) {
bool initialized = false;
for (size_t i = 0; i < 9; i++) if (scr_bytes_ctors[i]) initialized = true;
for (size_t i = 0; i < SCR_BYTES_ELEM_COUNT; i++) if (scr_bytes_ctors[i]) initialized = true;
if (!initialized) scr_atexit(scr_bytes_ctors_cleanup);
scr_bytes_ctors[elem] = scr_dyn_new_func(scr_closure_new(NULL, 0),
scr_bytes_ctor_call, 3, "typed-array-constructor", scr_bytes_elem_name(elem));
@@ -3241,7 +3240,7 @@ static ScrDyn *scr_bytes_constructor_for(ScrBytesElem elem) {
return scr_dyn_retain(scr_bytes_ctors[elem]);
}
ScrDyn *scr_bytes_constructor(const ScrStr *name) {
for (size_t i = 0; i < 9; i++) {
for (size_t i = 0; i < SCR_BYTES_ELEM_COUNT; i++) {
const char *candidate = scr_bytes_elem_name((ScrBytesElem)i);
if (strlen(candidate) == name->len && memcmp(candidate, name->data, name->len) == 0)
return scr_bytes_constructor_for((ScrBytesElem)i);
@@ -3308,7 +3307,7 @@ bool scr_bytes_instanceof(const ScrDyn *value, const ScrDyn *callee) {
}
if (scr_array_buffer_ctor && callee->v.fn.clo == scr_array_buffer_ctor->v.fn.clo) return scr_array_buffer_is(value);
if (scr_array_ctor && callee->v.fn.clo == scr_array_ctor->v.fn.clo) return value->kind == SCR_DYN_ARR || scr_dyn_ref_is_array(value);
for (size_t i = 0; i < 9; i++) {
for (size_t i = 0; i < SCR_BYTES_ELEM_COUNT; i++) {
if (scr_bytes_ctors[i] && callee->v.fn.clo == scr_bytes_ctors[i]->v.fn.clo)
return scr_dyn_typed_array_is(value, (int)i);
}
@@ -3321,7 +3320,7 @@ ScrDyn *scr_bytes_construct(const ScrDyn *callee, const ScrDyn *args, const ScrS
return scr_array_ctor_call(callee->v.fn.clo, args->v.arr.items, args->v.arr.len);
if (callee->kind == SCR_DYN_FUNC && scr_array_buffer_ctor && callee->v.fn.clo == scr_array_buffer_ctor->v.fn.clo)
return scr_array_buffer_new(args->v.arr.len ? args->v.arr.items[0] : scr_dyn_undefined());
for (size_t i = 0; i < 9; i++) {
for (size_t i = 0; i < SCR_BYTES_ELEM_COUNT; i++) {
if (callee->kind != SCR_DYN_FUNC || !scr_bytes_ctors[i] || callee->v.fn.clo != scr_bytes_ctors[i]->v.fn.clo) continue;
const ScrDyn *input = args->v.arr.len > 0 ? args->v.arr.items[0] : scr_dyn_undefined();
const ScrDyn *offset = args->v.arr.len > 1 ? args->v.arr.items[1] : scr_dyn_undefined();
@@ -12377,6 +12376,7 @@ SCR_ARRAY_BUFFER_VIEW(i16, SCR_BYTES_I16)
SCR_ARRAY_BUFFER_VIEW(u8, SCR_BYTES_U8)
SCR_ARRAY_BUFFER_VIEW(u32, SCR_BYTES_U32)
SCR_ARRAY_BUFFER_VIEW(i32, SCR_BYTES_I32)
SCR_ARRAY_BUFFER_VIEW(f16, SCR_BYTES_F16)
SCR_ARRAY_BUFFER_VIEW(f32, SCR_BYTES_F32)
SCR_ARRAY_BUFFER_VIEW(f64, SCR_BYTES_F64)
#undef SCR_ARRAY_BUFFER_VIEW
+3 -3
View File
@@ -1,9 +1,9 @@
// util.types brand checks over typed values: isDate, isSharedArrayBuffer,
// isMapIterator, isSetIterator and isKeyObject answer from the value's
// internal brand, and isBoxedPrimitive (with the per-kind boxed checks),
// isBigInt64Array, isBigUint64Array, isFloat16Array, isCryptoKey and
// isExternal answer false for every value a program here can create. A
// Date-typed argument answers from its static type.
// isBigInt64Array, isBigUint64Array, isCryptoKey and isExternal answer
// false for every value a program here can create. A Date-typed argument
// answers from its static type.
import { types } from "node:util";
import * as direct from "node:util/types";
import { createSecretKey } from "node:crypto";
+94
View File
@@ -0,0 +1,94 @@
// Float16Array: construction from lengths, arrays, typed arrays and ArrayBuffer
// views; round-to-nearest-even stores with overflow, NaN, -0 and subnormals;
// fill, set, subarray, slice and both sort orders; DataView getFloat16 and
// setFloat16 in both byte orders; Math.f16round; util.types and instanceof;
// structuredClone, assert and Buffer views. Node.js prints the same.
import { types } from "node:util";
import assert from "node:assert";
const show = (label: string, a: Float16Array): void => console.log(label, a.length, Array.from(a).join(","));
const a = new Float16Array(6);
a[0] = 1.5;
a[1] = 65519;
a[2] = 65520;
a[3] = 0.1;
a[4] = -0;
a[5] = 5.960464477539063e-8 / 2;
show("rounding", a);
const b = new Float16Array([NaN, Infinity, -Infinity, 1e-8, 2049, 2051, 4097, -1e6]);
show("specials", b);
console.log(Object.is(a[4], -0), a[6], a.length, a.byteLength, Number.isNaN(b[0]));
const c = Float16Array.from([3.14159, 2.71828, 1.41421]);
show("from", c);
const e = new Float16Array(c);
e[0] = 100;
show("copy", e);
show("source untouched", c);
const f32 = new Float32Array([1 / 3, 2 / 3]);
show("from f32", new Float16Array(f32));
const buf = new ArrayBuffer(8);
const view = new Float16Array(buf, 2, 2);
view[0] = 1;
view[1] = -2;
console.log("view bytes", Array.from(new Uint8Array(buf)).join(","), new Uint16Array(buf)[1]);
const g = new Float16Array(5);
g.fill(0.3);
show("fill", g);
g.set([1.1, 2.2], 3);
show("set", g);
show("subarray", g.subarray(1, 4));
show("slice", g.slice(-2));
const h = new Float16Array([3, -0, 0, NaN, -1, 2.5, -Infinity, 0.1]);
show("sort", h.slice().sort());
show("sort desc", h.slice().sort((x, y) => y - x));
show("toSorted", h.toSorted());
let sum = 0;
for (const v of c) sum += v;
console.log("for-of", sum, h.join("|"), String(c), Object.prototype.toString.call(c));
const dv = new DataView(new ArrayBuffer(8));
dv.setFloat16(0, 3.14159);
dv.setFloat16(2, 3.14159, true);
dv.setFloat16(4, -65504);
dv.setFloat16(6, NaN, true);
console.log("dataview", dv.getFloat16(0), dv.getFloat16(2, true), dv.getFloat16(4), dv.getFloat16(6, true), dv.getUint16(0), dv.getUint16(2));
try {
dv.getFloat16(7);
} catch (err) {
console.log((err as Error).name, (err as Error).message);
}
console.log("f16round", Math.f16round(1.337), Math.f16round(65520), Math.f16round(-0), Math.f16round(5e-8), Math.f16round(2 ** -25), Math.f16round(1.0009765625 + 2 ** -12));
console.log("types", types.isFloat16Array(a), types.isFloat32Array(a), types.isTypedArray(a), a instanceof Float16Array);
const u: unknown = a;
console.log("unknown", u instanceof Float16Array, u instanceof Float32Array);
const big = new Float16Array(1000);
for (let i = 0; i < big.length; i++) big[i] = Math.sin(i) * 1000;
let acc = 0;
for (let i = 0; i < big.length; i++) acc += big[i]!;
console.log("loop", acc, big[999]);
const f = new Float16Array([1.5, NaN, -0, 65504]);
const clone = structuredClone(f);
clone[0] = 2;
console.log(clone[0], f[0], clone.length);
assert.deepStrictEqual(new Float16Array([NaN, 1]), new Float16Array([NaN, 1]));
try {
assert.deepStrictEqual(new Float16Array([0]), new Float16Array([-0]));
} catch (e) {
console.log("deepStrictEqual -0", (e as Error).name);
}
assert.deepEqual(new Float16Array([0]), new Float16Array([-0]));
const raw = Buffer.from(f.buffer);
console.log(raw.length, raw.readUInt16LE(0), raw.readUInt16LE(6));
const fromBuf = new Float16Array(f.buffer, 2, 1);
console.log(Number.isNaN(fromBuf[0]));
const list: Float16Array[] = [f, new Float16Array(2)];
const m = new Map<string, Float16Array>([["a", f]]);
console.log(list.length, m.get("a")![3], list[1]!.length);
const Ctor = Float16Array;
const x = new Ctor(3);
x[1] = 0.2;
console.log(x.length, x[1]);
const y = new Float16Array([1, 2]);
const D = y.constructor as Float16ArrayConstructor;
const z = new D(2);
console.log(z.length, z instanceof Float16Array);
const unknownView: unknown = new Float16Array([7]);
console.log(unknownView instanceof Float16Array);
+95
View File
@@ -0,0 +1,95 @@
// Float16Array from JavaScript: construction from lengths, arrays, typed
// arrays and ArrayBuffer views; round-to-nearest-even stores with overflow,
// NaN, -0 and subnormals; fill, set, subarray, slice and both sort orders;
// DataView getFloat16 and setFloat16 in both byte orders; Math.f16round;
// util.types and instanceof; structuredClone, assert and Buffer views.
// Node.js prints the same.
import { types } from "node:util";
import assert from "node:assert";
const show = (label , a ) => console.log(label, a.length, Array.from(a).join(","));
const a = new Float16Array(6);
a[0] = 1.5;
a[1] = 65519;
a[2] = 65520;
a[3] = 0.1;
a[4] = -0;
a[5] = 5.960464477539063e-8 / 2;
show("rounding", a);
const b = new Float16Array([NaN, Infinity, -Infinity, 1e-8, 2049, 2051, 4097, -1e6]);
show("specials", b);
console.log(Object.is(a[4], -0), a[6], a.length, a.byteLength, Number.isNaN(b[0]));
const c = Float16Array.from([3.14159, 2.71828, 1.41421]);
show("from", c);
const e = new Float16Array(c);
e[0] = 100;
show("copy", e);
show("source untouched", c);
const f32 = new Float32Array([1 / 3, 2 / 3]);
show("from f32", new Float16Array(f32));
const buf = new ArrayBuffer(8);
const view = new Float16Array(buf, 2, 2);
view[0] = 1;
view[1] = -2;
console.log("view bytes", Array.from(new Uint8Array(buf)).join(","), new Uint16Array(buf)[1]);
const g = new Float16Array(5);
g.fill(0.3);
show("fill", g);
g.set([1.1, 2.2], 3);
show("set", g);
show("subarray", g.subarray(1, 4));
show("slice", g.slice(-2));
const h = new Float16Array([3, -0, 0, NaN, -1, 2.5, -Infinity, 0.1]);
show("sort", h.slice().sort());
show("sort desc", h.slice().sort((x, y) => y - x));
show("toSorted", h.toSorted());
let sum = 0;
for (const v of c) sum += v;
console.log("for-of", sum, h.join("|"), String(c), Object.prototype.toString.call(c));
const dv = new DataView(new ArrayBuffer(8));
dv.setFloat16(0, 3.14159);
dv.setFloat16(2, 3.14159, true);
dv.setFloat16(4, -65504);
dv.setFloat16(6, NaN, true);
console.log("dataview", dv.getFloat16(0), dv.getFloat16(2, true), dv.getFloat16(4), dv.getFloat16(6, true), dv.getUint16(0), dv.getUint16(2));
try {
dv.getFloat16(7);
} catch (err) {
console.log((err ).name, (err ).message);
}
console.log("f16round", Math.f16round(1.337), Math.f16round(65520), Math.f16round(-0), Math.f16round(5e-8), Math.f16round(2 ** -25), Math.f16round(1.0009765625 + 2 ** -12));
console.log("types", types.isFloat16Array(a), types.isFloat32Array(a), types.isTypedArray(a), a instanceof Float16Array);
const u = a;
console.log("unknown", u instanceof Float16Array, u instanceof Float32Array);
const big = new Float16Array(1000);
for (let i = 0; i < big.length; i++) big[i] = Math.sin(i) * 1000;
let acc = 0;
for (let i = 0; i < big.length; i++) acc += big[i] ;
console.log("loop", acc, big[999]);
const f = new Float16Array([1.5, NaN, -0, 65504]);
const clone = structuredClone(f);
clone[0] = 2;
console.log(clone[0], f[0], clone.length);
assert.deepStrictEqual(new Float16Array([NaN, 1]), new Float16Array([NaN, 1]));
try {
assert.deepStrictEqual(new Float16Array([0]), new Float16Array([-0]));
} catch (e) {
console.log("deepStrictEqual -0", (e ).name);
}
assert.deepEqual(new Float16Array([0]), new Float16Array([-0]));
const raw = Buffer.from(f.buffer);
console.log(raw.length, raw.readUInt16LE(0), raw.readUInt16LE(6));
const fromBuf = new Float16Array(f.buffer, 2, 1);
console.log(Number.isNaN(fromBuf[0]));
const list = [f, new Float16Array(2)];
const m = new Map ([["a", f]]);
console.log(list.length, m.get("a") [3], list[1] .length);
const Ctor = Float16Array;
const x = new Ctor(3);
x[1] = 0.2;
console.log(x.length, x[1]);
const y = new Float16Array([1, 2]);
const D = y.constructor ;
const z = new D(2);
console.log(z.length, z instanceof Float16Array);
const unknownView = new Float16Array([7]);
console.log(unknownView instanceof Float16Array);
+3 -1
View File
@@ -1,6 +1,8 @@
// Stored ArrayBuffers and DataViews lower; Float16 accessors remain unsupported.
// Stored ArrayBuffers and DataViews lower, Float16 accessors included;
// detached accessor methods remain unsupported.
const buffer = new ArrayBuffer(16);
const view = new DataView(buffer);
view.setFloat16(0, 1.5);
console.log(view.getFloat16(0));
const getter = view.getFloat16;
// End of the diagnostic fixture.
+3 -10
View File
@@ -1,9 +1,8 @@
// The es2025-lib surface WITHOUT a lowering fences by name: resizable
// ArrayBuffers and transfer, SharedArrayBuffer.grow, Atomics.waitAsync,
// Float16Array and its Math/DataView companions, first-class iterator
// objects, and the unlowered forms of the implemented statics
// (Promise.try with fewer arguments than parameters, groupBy's
// literal-union keys, a set-like non-Set argument).
// first-class iterator objects, and the unlowered forms of the
// implemented statics (Promise.try with fewer arguments than parameters,
// groupBy's literal-union keys, a set-like non-Set argument).
// Resizable ArrayBuffer + transfer (ES2024).
const ab = new ArrayBuffer(8, { maxByteLength: 16 });
@@ -22,12 +21,6 @@ sab.grow(16);
const ia = new Int32Array(4);
const waited = Atomics.waitAsync(ia, 0, 0, 1);
// Float16 (ES2025): the typed array, Math.f16round, DataView accessors.
const f16 = new Float16Array(4);
console.log(Math.f16round(1.337));
const dv = new DataView(new Uint8Array(8).buffer);
dv.setFloat16(0, 1.5);
// First-class iterator objects.
const it = Iterator.from([1, 2, 3].values());
+5 -14
View File
@@ -1,17 +1,8 @@
dataview.ts:4:1 - error SC2020: 'DataView<ArrayBuffer>.setFloat16' is part of the standard library types but has no scriptc lowering yet
dataview.ts:7:16 - error SC2020: 'DataView<ArrayBuffer>.getFloat16' is part of the standard library types but has no scriptc lowering yet
3 | const view = new DataView(buffer);
4 | view.setFloat16(0, 1.5);
| ^~~~~~~~~~~~~~~
5 | console.log(view.getFloat16(0));
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
dataview.ts:5:13 - error SC2020: 'DataView<ArrayBuffer>.getFloat16' is part of the standard library types but has no scriptc lowering yet
4 | view.setFloat16(0, 1.5);
5 | console.log(view.getFloat16(0));
| ^~~~~~~~~~~~~~~
6 | // End of the diagnostic fixture.
6 | console.log(view.getFloat16(0));
7 | const getter = view.getFloat16;
| ^~~~~~~~~~~~~~~
8 | // End of the diagnostic fixture.
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
@@ -1,125 +1,89 @@
es2025-fences.ts:9:12 - error SC2020: 'resizable ArrayBuffer construction' is part of the standard library types but has no scriptc lowering yet
es2025-fences.ts:8:12 - error SC2020: 'resizable ArrayBuffer construction' is part of the standard library types but has no scriptc lowering yet
8 | // Resizable ArrayBuffer + transfer (ES2024).
9 | const ab = new ArrayBuffer(8, { maxByteLength: 16 });
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10 | ab.resize(16);
7 | // Resizable ArrayBuffer + transfer (ES2024).
8 | const ab = new ArrayBuffer(8, { maxByteLength: 16 });
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9 | ab.resize(16);
hint: fixed-length ArrayBuffers accept a byte length; resizable storage is not supported yet
es2025-fences.ts:15:27 - error SC2020: 'resizable ArrayBuffer construction' is part of the standard library types but has no scriptc lowering yet
es2025-fences.ts:14:27 - error SC2020: 'resizable ArrayBuffer construction' is part of the standard library types but has no scriptc lowering yet
14 | // fixed-length new Uint8Array(new ArrayBuffer(8)) lowers).
15 | const rz = new Uint8Array(new ArrayBuffer(8, { maxByteLength: 16 }));
13 | // fixed-length new Uint8Array(new ArrayBuffer(8)) lowers).
14 | const rz = new Uint8Array(new ArrayBuffer(8, { maxByteLength: 16 }));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 |
15 |
hint: fixed-length ArrayBuffers accept a byte length; resizable storage is not supported yet
es2025-fences.ts:18:13 - error SC2020: 'resizable ArrayBuffer construction' is part of the standard library types but has no scriptc lowering yet
es2025-fences.ts:17:13 - error SC2020: 'resizable ArrayBuffer construction' is part of the standard library types but has no scriptc lowering yet
17 | // SharedArrayBuffer growth.
18 | const sab = new SharedArrayBuffer(8, { maxByteLength: 16 });
16 | // SharedArrayBuffer growth.
17 | const sab = new SharedArrayBuffer(8, { maxByteLength: 16 });
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19 | sab.grow(16);
18 | sab.grow(16);
hint: fixed-length ArrayBuffers accept a byte length; resizable storage is not supported yet
es2025-fences.ts:23:7 - error SC2009: values of type '{ async: false; value: "not-equal" | "timed-out"; } | { async: true; value: Promise<"ok" | "timed-out">; }' cannot be compiled: the union shape is supported, but its arm '{ async: false; value: "not-equal" | "timed-out"; }' does not compile
es2025-fences.ts:22:7 - error SC2009: values of type '{ async: false; value: "not-equal" | "timed-out"; } | { async: true; value: Promise<"ok" | "timed-out">; }' cannot be compiled: the union shape is supported, but its arm '{ async: false; value: "not-equal" | "timed-out"; }' does not compile
22 | const ia = new Int32Array(4);
23 | const waited = Atomics.waitAsync(ia, 0, 0, 1);
21 | const ia = new Int32Array(4);
22 | const waited = Atomics.waitAsync(ia, 0, 0, 1);
| ^~~~~~
24 |
23 |
hint: the outer shape is supported — the named component is the blocker; restate it inside the compilable set (number, string, boolean, arrays, Maps, Sets, RegExp, functions, classes, records, unions of those, and 'unknown')
es2025-fences.ts:23:16 - error SC2020: 'Atomics.waitAsync' is part of the standard library types but has no scriptc lowering yet
es2025-fences.ts:22:16 - error SC2020: 'Atomics.waitAsync' is part of the standard library types but has no scriptc lowering yet
22 | const ia = new Int32Array(4);
23 | const waited = Atomics.waitAsync(ia, 0, 0, 1);
21 | const ia = new Int32Array(4);
22 | const waited = Atomics.waitAsync(ia, 0, 0, 1);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24 |
23 |
hint: use integer load, store, exchange, compareExchange, add, sub, and, or, xor, wait or notify
es2025-fences.ts:26:7 - error SC2020: 'Float16Array<ArrayBuffer>' is part of the standard library types but has no scriptc lowering yet
es2025-fences.ts:25:7 - error SC2020: 'IteratorObject<number, undefined, unknown>' is part of the standard library types but has no scriptc lowering yet
25 | // Float16 (ES2025): the typed array, Math.f16round, DataView accessors.
26 | const f16 = new Float16Array(4);
| ^~~
27 | console.log(Math.f16round(1.337));
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
es2025-fences.ts:26:13 - error SC2020: 'new Float16Array' is part of the standard library types but has no scriptc lowering yet
25 | // Float16 (ES2025): the typed array, Math.f16round, DataView accessors.
26 | const f16 = new Float16Array(4);
| ^~~~~~~~~~~~~~~~~~~
27 | console.log(Math.f16round(1.337));
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
es2025-fences.ts:27:13 - error SC2020: 'Math.f16round' is part of the standard library types but has no scriptc lowering yet
26 | const f16 = new Float16Array(4);
27 | console.log(Math.f16round(1.337));
| ^~~~~~~~~~~~~
28 | const dv = new DataView(new Uint8Array(8).buffer);
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
es2025-fences.ts:29:1 - error SC2020: 'DataView<ArrayBuffer>.setFloat16' is part of the standard library types but has no scriptc lowering yet
28 | const dv = new DataView(new Uint8Array(8).buffer);
29 | dv.setFloat16(0, 1.5);
| ^~~~~~~~~~~~~
30 |
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
es2025-fences.ts:32:7 - error SC2020: 'IteratorObject<number, undefined, unknown>' is part of the standard library types but has no scriptc lowering yet
31 | // First-class iterator objects.
32 | const it = Iterator.from([1, 2, 3].values());
24 | // First-class iterator objects.
25 | const it = Iterator.from([1, 2, 3].values());
| ^~
33 |
26 |
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
es2025-fences.ts:32:12 - error SC2020: 'Iterator.from' is part of the standard library types but has no scriptc lowering yet
es2025-fences.ts:25:12 - error SC2020: 'Iterator.from' is part of the standard library types but has no scriptc lowering yet
31 | // First-class iterator objects.
32 | const it = Iterator.from([1, 2, 3].values());
24 | // First-class iterator objects.
25 | const it = Iterator.from([1, 2, 3].values());
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
33 |
26 |
hint: first-class iterator objects have no lowering — iterator helpers compile as one chain on an array iterator, consumed in place: arr.values().map(f).take(n).toArray()
es2025-fences.ts:35:27 - error SC2020: 'Promise.try over a callback of type '(a: number, b?: number | undefined) => number' with 1 argument' is part of the standard library types but has no scriptc lowering yet
es2025-fences.ts:28:27 - error SC2020: 'Promise.try over a callback of type '(a: number, b?: number | undefined) => number' with 1 argument' is part of the standard library types but has no scriptc lowering yet
34 | // Promise.try with fewer arguments than the callback's parameters.
35 | const tried = Promise.try((a: number, b?: number) => a + (b ?? 0), 1);
27 | // Promise.try with fewer arguments than the callback's parameters.
28 | const tried = Promise.try((a: number, b?: number) => a + (b ?? 0), 1);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
36 |
29 |
hint: pass one argument per callback parameter: Promise.try((a, b) => a + b, 1, 2)
es2025-fences.ts:38:17 - error SC2020: 'Object.groupBy at this result type' is part of the standard library types but has no scriptc lowering yet
es2025-fences.ts:31:17 - error SC2020: 'Object.groupBy at this result type' is part of the standard library types but has no scriptc lowering yet
37 | // Object.groupBy at a literal-union key type (a fixed-field record).
38 | const grouped = Object.groupBy([1, 2, 3], (n) => (n % 2 === 0 ? "even" : "odd"));
30 | // Object.groupBy at a literal-union key type (a fixed-field record).
31 | const grouped = Object.groupBy([1, 2, 3], (n) => (n % 2 === 0 ? "even" : "odd"));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
39 |
32 |
hint: a string-keyed result (Partial<Record<string, T[]>>) is the supported form — a literal-union key type makes a fixed-field record: widen the callback's key type to string (numbers stringify like JS property keys)
es2025-fences.ts:43:19 - error SC2020: 'Set.prototype.union over a set-like argument of type '{ size: number; has: (x: number) => x is 1; keys: () => ArrayIterator<number>; }'' is part of the standard library types but has no scriptc lowering yet
es2025-fences.ts:36:19 - error SC2020: 'Set.prototype.union over a set-like argument of type '{ size: number; has: (x: number) => x is 1; keys: () => ArrayIterator<number>; }'' is part of the standard library types but has no scriptc lowering yet
42 | const like = { size: 1, has: (x: number) => x === 1, keys: () => [1].values() };
43 | const u = s.union(like);
35 | const like = { size: 1, has: (x: number) => x === 1, keys: () => [1].values() };
36 | const u = s.union(like);
| ^~~~
44 |
37 |
hint: only a real Set or Map lowers (the argument's has/size/keys must be the builtins) — build one first: new Set(...)
+4
View File
@@ -1860,6 +1860,8 @@
"5886-inspect-dates.ts": 2559,
"5887-inspect-typed-arrays.ts": 3448,
"5888-inspect-sorted.ts": 2998,
"5900-float16-array.ts": 3000,
"5901-float16-array-js.js": 2900,
"5904-async-generator-completion.ts": 2950,
"5905-array-from-async-generator.ts": 2750,
"5906-array-from-async-generator-js.js": 2950,
@@ -4523,6 +4525,8 @@
"5886-inspect-dates.ts": 2736,
"5887-inspect-typed-arrays.ts": 2997,
"5888-inspect-sorted.ts": 2861,
"5900-float16-array.ts": 3750,
"5901-float16-array-js.js": 3700,
"5904-async-generator-completion.ts": 3800,
"5905-array-from-async-generator.ts": 3500,
"5906-array-from-async-generator-js.js": 3750,