import { assert } from '../../../../../../common/util/util.js';
import { Comparator, alwaysPass, anyOf } from '../../../../../util/compare.js';
import { kBit, kValue } from '../../../../../util/constants.js';
import {
ScalarValue,
VectorValue,
f16,
f32,
i32,
toVector,
u32,
abstractFloat,
abstractInt,
} from '../../../../../util/conversion.js';
import { FP, FPInterval } from '../../../../../util/floating_point.js';
import {
cartesianProduct,
fullI32Range,
fullU32Range,
isFiniteF16,
isFiniteF32,
isSubnormalNumberF16,
isSubnormalNumberF32,
linearRange,
scalarF16Range,
scalarF32Range,
} from '../../../../../util/math.js';
import {
reinterpretF16AsU16,
reinterpretF32AsI32,
reinterpretF32AsU32,
reinterpretI32AsF32,
reinterpretI32AsU32,
reinterpretU16AsF16,
reinterpretU32AsF32,
reinterpretU32AsI32,
} from '../../../../../util/reinterpret.js';
import { makeCaseCache } from '../../case_cache.js';
const numNaNs = 11;
const f32InfAndNaNInU32: number[] = [
...linearRange(kBit.f32.positive.infinity + 1, kBit.i32.positive.max, numNaNs),
...linearRange(kBit.f32.negative.infinity + 1, kBit.u32.max, numNaNs),
kBit.f32.positive.infinity,
kBit.f32.negative.infinity,
];
const f32InfAndNaNInF32 = f32InfAndNaNInU32.map(u => reinterpretU32AsF32(u));
const f32InfAndNaNInI32 = f32InfAndNaNInU32.map(u => reinterpretU32AsI32(u));
const f32ZerosInU32 = [0, kBit.f32.negative.zero];
const f32ZerosInF32 = f32ZerosInU32.map(u => reinterpretU32AsF32(u));
const f32ZerosInI32 = f32ZerosInU32.map(u => reinterpretU32AsI32(u));
const f32ZerosInterval: FPInterval = new FPInterval('f32', -0.0, 0.0);
const f32FiniteRange: number[] = [...scalarF32Range(), kValue.f32.negative.zero];
const f32RangeWithInfAndNaN: number[] = [...f32FiniteRange, ...f32InfAndNaNInF32];
const f16FiniteInF16: number[] = [...scalarF16Range(), kValue.f16.negative.zero];
const f16FiniteInU16: number[] = f16FiniteInF16.map(u => reinterpretF16AsU16(u));
const f16InfAndNaNInU16: number[] = [
...linearRange(kBit.f16.positive.infinity + 1, 32767, numNaNs).map(v => Math.ceil(v)),
...linearRange(kBit.f16.negative.infinity + 1, 65535, numNaNs).map(v => Math.floor(v)),
kBit.f16.positive.infinity,
kBit.f16.negative.infinity,
];
const f16InfAndNaNInF16 = f16InfAndNaNInU16.map(u => reinterpretU16AsF16(u));
const f16ZerosInU16 = [kBit.f16.negative.zero, 0];
const f16ZerosInterval: FPInterval = new FPInterval('f16', -0.0, 0.0);
* @returns an u32 whose lower and higher 16bits are the two elements of the
* given array of two u16 respectively, in little-endian.
*/
function u16x2ToU32(u16x2: readonly number[]): number {
assert(u16x2.length === 2);
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
view.setUint16(0, u16x2[0], true);
view.setUint16(2, u16x2[1], true);
return view.getUint32(0, true);
}
* @returns an array of two u16, respectively the lower and higher 16bits of
* given u32 in little-endian.
*/
function u32ToU16x2(u32: number): number[] {
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
view.setUint32(0, u32, true);
return [view.getUint16(0, true), view.getUint16(2, true)];
}
* @returns a vec2<f16> from an array of two u16, each reinterpreted as f16.
*/
function u16x2ToVec2F16(u16x2: number[]): VectorValue {
assert(u16x2.length === 2);
return toVector(u16x2.map(reinterpretU16AsF16), f16);
}
* @returns a vec4<f16> from an array of four u16, each reinterpreted as f16.
*/
function u16x4ToVec4F16(u16x4: number[]): VectorValue {
assert(u16x4.length === 4);
return toVector(u16x4.map(reinterpretU16AsF16), f16);
}
* @returns true if and only if a given u32 can bitcast to a vec2<f16> with all elements
* being finite f16 values.
*/
function canU32BitcastToFiniteVec2F16(u32: number): boolean {
return u32ToU16x2(u32)
.map(u16 => isFiniteF16(reinterpretU16AsF16(u16)))
.reduce((a, b) => a && b, true);
}
* @returns an array of N elements with the i-th element being an array of len elements
* [a_i, a_((i+1)%N), ..., a_((i+len-1)%N)], for the input array of N element [a_1, ... a_N]
* and the given len. For example, slidingSlice([1, 2, 3], 2) result in
* [[1, 2], [2, 3], [3, 1]].
* This helper function is used for generating vector cases from scalar values array.
*/
function slidingSlice(input: number[], len: number) {
const result: number[][] = [];
for (let i = 0; i < input.length; i++) {
const sub: number[] = [];
for (let j = 0; j < len; j++) {
sub.push(input[(i + j) % input.length]);
}
result.push(sub);
}
return result;
}
const f16Vec2InfAndNaNInU32 = [
...cartesianProduct(f16InfAndNaNInU16, [...f16InfAndNaNInU16, ...f16FiniteInU16]),
...cartesianProduct(f16FiniteInU16, f16InfAndNaNInU16),
].map(u16x2ToU32);
const f16Vec2InfAndNaNInI32 = f16Vec2InfAndNaNInU32.map(u => reinterpretU32AsI32(u));
const f16Vec2ZerosInU32 = cartesianProduct(f16ZerosInU16, f16ZerosInU16).map(u16x2ToU32);
const f16Vec2ZerosInI32 = f16Vec2ZerosInU32.map(u => reinterpretU32AsI32(u));
const u32RangeForF16Vec2FiniteInfNaN: number[] = [
...fullU32Range(),
...f16Vec2ZerosInU32,
...f16Vec2InfAndNaNInU32,
];
const u32RangeForF16Vec2Finite: number[] = u32RangeForF16Vec2FiniteInfNaN.filter(
canU32BitcastToFiniteVec2F16
);
const i32RangeForF16Vec2FiniteInfNaN: number[] = [
...fullI32Range(),
...f16Vec2ZerosInI32,
...f16Vec2InfAndNaNInI32,
];
const i32RangeForF16Vec2Finite: number[] = i32RangeForF16Vec2FiniteInfNaN.filter(u =>
canU32BitcastToFiniteVec2F16(reinterpretI32AsU32(u))
);
const f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN: number[] = [
...f32RangeWithInfAndNaN,
...u32RangeForF16Vec2FiniteInfNaN.map(reinterpretU32AsF32),
];
const f32FiniteRangeForF16Vec2Finite: number[] = f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN
.filter(isFiniteF32)
.filter(u => canU32BitcastToFiniteVec2F16(reinterpretF32AsU32(u)));
const f16Vec2FiniteInU16x2 = slidingSlice(f16FiniteInU16, 2);
const f16Vec2FiniteInfNanInU16x2 = slidingSlice([...f16FiniteInU16, ...f16InfAndNaNInU16], 2);
const f16Vec2FiniteInU16x4 = slidingSlice(f16FiniteInU16, 4);
const f16Vec2FiniteInfNanInU16x4 = slidingSlice([...f16FiniteInU16, ...f16InfAndNaNInU16], 4);
const anyF32 = alwaysPass('any f32');
const anyI32 = alwaysPass('any i32');
const anyU32 = alwaysPass('any u32');
const f32UnboundedInterval = FP.f32.constants().unboundedInterval;
const f16UnboundedInterval = FP.f16.constants().unboundedInterval;
const i32RangeForF32FiniteInfNaN: number[] = [
...fullI32Range(),
...f32ZerosInI32,
...f32InfAndNaNInI32,
];
const i32RangeForF32Finite: number[] = i32RangeForF32FiniteInfNaN.filter(i =>
isFiniteF32(reinterpretI32AsF32(i))
);
const u32RangeForF32FiniteInfNaN: number[] = [
...fullU32Range(),
...f32ZerosInU32,
...f32InfAndNaNInU32,
];
const u32RangeForF32Finite: number[] = u32RangeForF32FiniteInfNaN.filter(u =>
isFiniteF32(reinterpretU32AsF32(u))
);
* @returns a Comparator for checking if a f32 value is a valid
* bitcast conversion from f32.
*/
function bitcastF32ToF32Comparator(f: number): Comparator {
if (!isFiniteF32(f)) return anyF32;
const acceptable: number[] = [f, ...(isSubnormalNumberF32(f) ? f32ZerosInF32 : [])];
return anyOf(...acceptable.map(f32));
}
* @returns a Comparator for checking if a u32 value is a valid
* bitcast conversion from f32.
*/
function bitcastF32ToU32Comparator(f: number): Comparator {
if (!isFiniteF32(f)) return anyU32;
const acceptable: number[] = [
reinterpretF32AsU32(f),
...(isSubnormalNumberF32(f) ? f32ZerosInU32 : []),
];
return anyOf(...acceptable.map(u32));
}
* @returns a Comparator for checking if a i32 value is a valid
* bitcast conversion from f32.
*/
function bitcastF32ToI32Comparator(f: number): Comparator {
if (!isFiniteF32(f)) return anyI32;
const acceptable: number[] = [
reinterpretF32AsI32(f),
...(isSubnormalNumberF32(f) ? f32ZerosInI32 : []),
];
return anyOf(...acceptable.map(i32));
}
* @returns a Comparator for checking if a f32 value is a valid
* bitcast conversion from i32.
*/
function bitcastI32ToF32Comparator(i: number): Comparator {
const f: number = reinterpretI32AsF32(i);
if (!isFiniteF32(f)) return anyI32;
if (f32ZerosInI32.includes(i)) return anyOf(...f32ZerosInF32.map(f32));
const acceptable: number[] = [f, ...(isSubnormalNumberF32(f) ? f32ZerosInF32 : [])];
return anyOf(...acceptable.map(f32));
}
* @returns a Comparator for checking if a f32 value is a valid
* bitcast conversion from u32.
*/
function bitcastU32ToF32Comparator(u: number): Comparator {
const f: number = reinterpretU32AsF32(u);
if (!isFiniteF32(f)) return anyU32;
if (f32ZerosInU32.includes(u)) return anyOf(...f32ZerosInF32.map(f32));
const acceptable: number[] = [f, ...(isSubnormalNumberF32(f) ? f32ZerosInF32 : [])];
return anyOf(...acceptable.map(f32));
}
* @returns an array of expected f16 FPInterval for the given bitcasted f16 value, which may be
* subnormal, Inf, or NaN. Test cases that bitcasted to vector of f16 use this function to get
* per-element expectation and build vector expectation using cartesianProduct.
*/
function generateF16ExpectationIntervals(bitcastedF16Value: number): FPInterval[] {
if (!isFiniteF16(bitcastedF16Value)) {
return [f16UnboundedInterval];
}
if (bitcastedF16Value === 0.0) {
return [f16ZerosInterval];
}
const exactInterval = FP.f16.toInterval(bitcastedF16Value);
return [exactInterval, ...(isSubnormalNumberF16(bitcastedF16Value) ? [f16ZerosInterval] : [])];
}
* @returns a Comparator for checking if a f16 value is a valid
* bitcast conversion from f16.
*/
function bitcastF16ToF16Comparator(f: number): Comparator {
if (!isFiniteF16(f)) return anyOf(f16UnboundedInterval);
return anyOf(...generateF16ExpectationIntervals(f));
}
* @returns a Comparator for checking if a vec2<f16> is a valid bitcast
* conversion from u32.
*/
function bitcastU32ToVec2F16Comparator(u: number): Comparator {
const bitcastedVec2F16InU16x2 = u32ToU16x2(u).map(reinterpretU16AsF16);
const expectedIntervalsCombination = cartesianProduct(
...bitcastedVec2F16InU16x2.map(generateF16ExpectationIntervals)
);
return anyOf(...expectedIntervalsCombination);
}
* @returns a Comparator for checking if a vec2<f16> value is a valid
* bitcast conversion from i32.
*/
function bitcastI32ToVec2F16Comparator(i: number): Comparator {
const bitcastedVec2F16InU16x2 = u32ToU16x2(reinterpretI32AsU32(i)).map(reinterpretU16AsF16);
const expectedIntervalsCombination = cartesianProduct(
...bitcastedVec2F16InU16x2.map(generateF16ExpectationIntervals)
);
return anyOf(...expectedIntervalsCombination);
}
* @returns a Comparator for checking if a vec2<f16> value is a valid
* bitcast conversion from f32.
*/
function bitcastF32ToVec2F16Comparator(f: number): Comparator {
if (!isFiniteF32(f)) {
return anyOf([f16UnboundedInterval, f16UnboundedInterval]);
}
const bitcastedVec2F16InU16x2 = u32ToU16x2(reinterpretF32AsU32(f)).map(reinterpretU16AsF16);
const expectedIntervalsCombination = cartesianProduct(
...bitcastedVec2F16InU16x2.map(generateF16ExpectationIntervals)
);
return anyOf(...expectedIntervalsCombination);
}
* @returns a Comparator for checking if a vec4<f16> is a valid
* bitcast conversion from vec2<u32>.
*/
function bitcastVec2U32ToVec4F16Comparator(u32x2: number[]): Comparator {
assert(u32x2.length === 2);
const bitcastedVec4F16InU16x4 = u32x2.flatMap(u32ToU16x2).map(reinterpretU16AsF16);
const expectedIntervalsCombination = cartesianProduct(
...bitcastedVec4F16InU16x4.map(generateF16ExpectationIntervals)
);
return anyOf(...expectedIntervalsCombination);
}
* @returns a Comparator for checking if a vec4<f16> is a valid
* bitcast conversion from vec2<i32>.
*/
function bitcastVec2I32ToVec4F16Comparator(i32x2: number[]): Comparator {
assert(i32x2.length === 2);
const bitcastedVec4F16InU16x4 = i32x2
.map(reinterpretI32AsU32)
.flatMap(u32ToU16x2)
.map(reinterpretU16AsF16);
const expectedIntervalsCombination = cartesianProduct(
...bitcastedVec4F16InU16x4.map(generateF16ExpectationIntervals)
);
return anyOf(...expectedIntervalsCombination);
}
* @returns a Comparator for checking if a vec4<f16> is a valid
* bitcast conversion from vec2<f32>.
*/
function bitcastVec2F32ToVec4F16Comparator(f32x2: number[]): Comparator {
assert(f32x2.length === 2);
const bitcastedVec4F16InU16x4 = f32x2
.map(reinterpretF32AsU32)
.flatMap(u32ToU16x2)
.map(reinterpretU16AsF16);
const expectedIntervalsCombination = cartesianProduct(
...bitcastedVec4F16InU16x4.map(generateF16ExpectationIntervals)
);
return anyOf(...expectedIntervalsCombination);
}
interface ExpectationFor32BitsScalarFromF16x2 {
possibleExpectations: (ScalarValue | FPInterval)[];
isUnbounded: boolean;
}
* @returns the array of possible 16bits, represented in u16, that bitcasted
* from a given finite f16 represented in u16, handling the possible subnormal
* flushing. Used to build up 32bits or larger results.
*/
function possibleBitsInU16FromFiniteF16InU16(f16InU16: number): number[] {
const h = reinterpretU16AsF16(f16InU16);
assert(isFiniteF16(h));
return [f16InU16, ...(isSubnormalNumberF16(h) ? f16ZerosInU16 : [])];
}
* @returns the expectation for a single 32bit scalar bitcasted from given pair of
* f16, result in ExpectationFor32BitsScalarFromF16x2.
*/
function possible32BitScalarIntervalsFromF16x2(
f16x2InU16x2: number[],
type: 'i32' | 'u32' | 'f32'
): ExpectationFor32BitsScalarFromF16x2 {
assert(f16x2InU16x2.length === 2);
let reinterpretFromU32: (x: number) => number;
let expectationsForValue: (x: number) => ScalarValue[] | FPInterval[];
let unboundedExpectations: FPInterval[] | ScalarValue[];
if (type === 'u32') {
reinterpretFromU32 = (x: number) => x;
expectationsForValue = x => [u32(x)];
unboundedExpectations = [u32(0)];
} else if (type === 'i32') {
reinterpretFromU32 = (x: number) => reinterpretU32AsI32(x);
expectationsForValue = x => [i32(x)];
unboundedExpectations = [i32(0)];
} else {
assert(type === 'f32');
reinterpretFromU32 = (x: number) => reinterpretU32AsF32(x);
expectationsForValue = x => {
if (!isFiniteF32(x)) {
return [f32UnboundedInterval];
}
if (x === 0.0) {
return [f32ZerosInterval];
}
const exactInterval = FP.f32.toInterval(x);
return [exactInterval, ...(isSubnormalNumberF32(x) ? [f32ZerosInterval] : [])];
};
unboundedExpectations = [f32UnboundedInterval];
}
if (
!isFiniteF16(reinterpretU16AsF16(f16x2InU16x2[0])) ||
!isFiniteF16(reinterpretU16AsF16(f16x2InU16x2[1]))
) {
return { possibleExpectations: unboundedExpectations, isUnbounded: true };
}
const possibleU16Bits = f16x2InU16x2.map(possibleBitsInU16FromFiniteF16InU16);
const possibleExpectations = cartesianProduct(...possibleU16Bits).flatMap<
ScalarValue | FPInterval
>((possibleBitsU16x2: readonly number[]) => {
assert(possibleBitsU16x2.length === 2);
return expectationsForValue(reinterpretFromU32(u16x2ToU32(possibleBitsU16x2)));
});
return { possibleExpectations, isUnbounded: false };
}
* @returns a Comparator for checking if a u32 value is a valid
* bitcast conversion from vec2 f16.
*/
function bitcastVec2F16ToU32Comparator(vec2F16InU16x2: number[]): Comparator {
assert(vec2F16InU16x2.length === 2);
const expectations = possible32BitScalarIntervalsFromF16x2(vec2F16InU16x2, 'u32');
if (expectations.isUnbounded) {
return anyU32;
}
return anyOf(...expectations.possibleExpectations);
}
* @returns a Comparator for checking if a i32 value is a valid
* bitcast conversion from vec2 f16.
*/
function bitcastVec2F16ToI32Comparator(vec2F16InU16x2: number[]): Comparator {
assert(vec2F16InU16x2.length === 2);
const expectations = possible32BitScalarIntervalsFromF16x2(vec2F16InU16x2, 'i32');
if (expectations.isUnbounded) {
return anyI32;
}
return anyOf(...expectations.possibleExpectations);
}
* @returns a Comparator for checking if a i32 value is a valid
* bitcast conversion from vec2 f16.
*/
function bitcastVec2F16ToF32Comparator(vec2F16InU16x2: number[]): Comparator {
assert(vec2F16InU16x2.length === 2);
const expectations = possible32BitScalarIntervalsFromF16x2(vec2F16InU16x2, 'f32');
if (expectations.isUnbounded) {
return anyF32;
}
return anyOf(...expectations.possibleExpectations);
}
* @returns a Comparator for checking if a vec2 u32 value is a valid
* bitcast conversion from vec4 f16.
*/
function bitcastVec4F16ToVec2U32Comparator(vec4F16InU16x4: number[]): Comparator {
assert(vec4F16InU16x4.length === 4);
const expectationsPerElement = [vec4F16InU16x4.slice(0, 2), vec4F16InU16x4.slice(2, 4)].map(e =>
possible32BitScalarIntervalsFromF16x2(e, 'u32')
);
if (expectationsPerElement.map(e => e.isUnbounded).reduce((a, b) => a || b, false)) {
return alwaysPass('any vec2<u32>');
}
return anyOf(
...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map(
e => new VectorValue(e as ScalarValue[])
)
);
}
* @returns a Comparator for checking if a vec2 i32 value is a valid
* bitcast conversion from vec4 f16.
*/
function bitcastVec4F16ToVec2I32Comparator(vec4F16InU16x4: number[]): Comparator {
assert(vec4F16InU16x4.length === 4);
const expectationsPerElement = [vec4F16InU16x4.slice(0, 2), vec4F16InU16x4.slice(2, 4)].map(e =>
possible32BitScalarIntervalsFromF16x2(e, 'i32')
);
if (expectationsPerElement.map(e => e.isUnbounded).reduce((a, b) => a || b, false)) {
return alwaysPass('any vec2<i32>');
}
return anyOf(
...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map(
e => new VectorValue(e as ScalarValue[])
)
);
}
* @returns a Comparator for checking if a vec2 f32 value is a valid
* bitcast conversion from vec4 f16.
*/
function bitcastVec4F16ToVec2F32Comparator(vec4F16InU16x4: number[]): Comparator {
assert(vec4F16InU16x4.length === 4);
const expectationsPerElement = [vec4F16InU16x4.slice(0, 2), vec4F16InU16x4.slice(2, 4)].map(e =>
possible32BitScalarIntervalsFromF16x2(e, 'f32')
);
return anyOf(
...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map(e => [
e[0] as FPInterval,
e[1] as FPInterval,
])
);
}
export const d = makeCaseCache('bitcast', {
i32_to_i32: () => fullI32Range().map(e => ({ input: i32(e), expected: i32(e) })),
u32_to_u32: () => fullU32Range().map(e => ({ input: u32(e), expected: u32(e) })),
f32_inf_nan_to_f32: () =>
f32RangeWithInfAndNaN.map(e => ({
input: f32(e),
expected: bitcastF32ToF32Comparator(e),
})),
f32_to_f32: () =>
f32FiniteRange.map(e => ({ input: f32(e), expected: bitcastF32ToF32Comparator(e) })),
f16_inf_nan_to_f16: () =>
[...f16FiniteInF16, ...f16InfAndNaNInF16].map(e => ({
input: f16(e),
expected: bitcastF16ToF16Comparator(e),
})),
f16_to_f16: () =>
f16FiniteInF16.map(e => ({ input: f16(e), expected: bitcastF16ToF16Comparator(e) })),
i32_to_u32: () => fullI32Range().map(e => ({ input: i32(e), expected: u32(e) })),
i32_to_f32: () =>
i32RangeForF32Finite.map(e => ({
input: i32(e),
expected: bitcastI32ToF32Comparator(e),
})),
ai_to_i32: () => fullI32Range().map(e => ({ input: abstractInt(BigInt(e)), expected: i32(e) })),
ai_to_u32: () => fullU32Range().map(e => ({ input: abstractInt(BigInt(e)), expected: u32(e) })),
ai_to_f32: () =>
i32RangeForF32Finite.map(e => ({
input: abstractInt(BigInt(e)),
expected: bitcastI32ToF32Comparator(e),
})),
i32_to_f32_inf_nan: () =>
i32RangeForF32FiniteInfNaN.map(e => ({
input: i32(e),
expected: bitcastI32ToF32Comparator(e),
})),
u32_to_i32: () => fullU32Range().map(e => ({ input: u32(e), expected: i32(e) })),
u32_to_f32: () =>
u32RangeForF32Finite.map(e => ({
input: u32(e),
expected: bitcastU32ToF32Comparator(e),
})),
u32_to_f32_inf_nan: () =>
u32RangeForF32FiniteInfNaN.map(e => ({
input: u32(e),
expected: bitcastU32ToF32Comparator(e),
})),
f32_inf_nan_to_i32: () =>
f32RangeWithInfAndNaN.map(e => ({
input: f32(e),
expected: bitcastF32ToI32Comparator(e),
})),
f32_to_i32: () =>
f32FiniteRange.map(e => ({ input: f32(e), expected: bitcastF32ToI32Comparator(e) })),
f32_inf_nan_to_u32: () =>
f32RangeWithInfAndNaN.map(e => ({
input: f32(e),
expected: bitcastF32ToU32Comparator(e),
})),
f32_to_u32: () =>
f32FiniteRange.map(e => ({ input: f32(e), expected: bitcastF32ToU32Comparator(e) })),
u32_to_vec2_f16_inf_nan: () =>
u32RangeForF16Vec2FiniteInfNaN.map(e => ({
input: u32(e),
expected: bitcastU32ToVec2F16Comparator(e),
})),
u32_to_vec2_f16: () =>
u32RangeForF16Vec2Finite.map(e => ({
input: u32(e),
expected: bitcastU32ToVec2F16Comparator(e),
})),
i32_to_vec2_f16_inf_nan: () =>
i32RangeForF16Vec2FiniteInfNaN.map(e => ({
input: i32(e),
expected: bitcastI32ToVec2F16Comparator(e),
})),
i32_to_vec2_f16: () =>
i32RangeForF16Vec2Finite.map(e => ({
input: i32(e),
expected: bitcastI32ToVec2F16Comparator(e),
})),
ai_to_vec2_f16: () =>
i32RangeForF16Vec2Finite.map(e => ({
input: abstractInt(BigInt(e)),
expected: bitcastI32ToVec2F16Comparator(e),
})),
f32_inf_nan_to_vec2_f16_inf_nan: () =>
f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN.map(e => ({
input: f32(e),
expected: bitcastF32ToVec2F16Comparator(e),
})),
f32_to_vec2_f16: () =>
f32FiniteRangeForF16Vec2Finite.map(e => ({
input: f32(e),
expected: bitcastF32ToVec2F16Comparator(e),
})),
af_to_vec2_f16: () =>
f32FiniteRangeForF16Vec2Finite.map(e => ({
input: abstractFloat(e),
expected: bitcastF32ToVec2F16Comparator(e),
})),
vec2_i32_to_vec4_f16_inf_nan: () =>
slidingSlice(i32RangeForF16Vec2FiniteInfNaN, 2).map(e => ({
input: toVector(e, i32),
expected: bitcastVec2I32ToVec4F16Comparator(e),
})),
vec2_i32_to_vec4_f16: () =>
slidingSlice(i32RangeForF16Vec2Finite, 2).map(e => ({
input: toVector(e, i32),
expected: bitcastVec2I32ToVec4F16Comparator(e),
})),
vec2_ai_to_vec4_f16: () =>
slidingSlice(i32RangeForF16Vec2Finite, 2).map(e => ({
input: toVector(e, (n: number) => abstractInt(BigInt(n))),
expected: bitcastVec2I32ToVec4F16Comparator(e),
})),
vec2_u32_to_vec4_f16_inf_nan: () =>
slidingSlice(u32RangeForF16Vec2FiniteInfNaN, 2).map(e => ({
input: toVector(e, u32),
expected: bitcastVec2U32ToVec4F16Comparator(e),
})),
vec2_u32_to_vec4_f16: () =>
slidingSlice(u32RangeForF16Vec2Finite, 2).map(e => ({
input: toVector(e, u32),
expected: bitcastVec2U32ToVec4F16Comparator(e),
})),
vec2_f32_inf_nan_to_vec4_f16_inf_nan: () =>
slidingSlice(f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN, 2).map(e => ({
input: toVector(e, f32),
expected: bitcastVec2F32ToVec4F16Comparator(e),
})),
vec2_f32_to_vec4_f16: () =>
slidingSlice(f32FiniteRangeForF16Vec2Finite, 2).map(e => ({
input: toVector(e, f32),
expected: bitcastVec2F32ToVec4F16Comparator(e),
})),
vec2_af_to_vec4_f16: () =>
slidingSlice(f32FiniteRangeForF16Vec2Finite, 2).map(e => ({
input: toVector(e, abstractFloat),
expected: bitcastVec2F32ToVec4F16Comparator(e),
})),
vec2_f16_to_u32: () =>
f16Vec2FiniteInU16x2.map(e => ({
input: u16x2ToVec2F16(e),
expected: bitcastVec2F16ToU32Comparator(e),
})),
vec2_f16_inf_nan_to_u32: () =>
f16Vec2FiniteInfNanInU16x2.map(e => ({
input: u16x2ToVec2F16(e),
expected: bitcastVec2F16ToU32Comparator(e),
})),
vec2_f16_to_i32: () =>
f16Vec2FiniteInU16x2.map(e => ({
input: u16x2ToVec2F16(e),
expected: bitcastVec2F16ToI32Comparator(e),
})),
vec2_f16_inf_nan_to_i32: () =>
f16Vec2FiniteInfNanInU16x2.map(e => ({
input: u16x2ToVec2F16(e),
expected: bitcastVec2F16ToI32Comparator(e),
})),
vec2_f16_to_f32_finite: () =>
f16Vec2FiniteInU16x2
.filter(u16x2 => isFiniteF32(reinterpretU32AsF32(u16x2ToU32(u16x2))))
.map(e => ({
input: u16x2ToVec2F16(e),
expected: bitcastVec2F16ToF32Comparator(e),
})),
vec2_f16_inf_nan_to_f32: () =>
f16Vec2FiniteInfNanInU16x2.map(e => ({
input: u16x2ToVec2F16(e),
expected: bitcastVec2F16ToF32Comparator(e),
})),
vec4_f16_to_vec2_u32: () =>
f16Vec2FiniteInU16x4.map(e => ({
input: u16x4ToVec4F16(e),
expected: bitcastVec4F16ToVec2U32Comparator(e),
})),
vec4_f16_inf_nan_to_vec2_u32: () =>
f16Vec2FiniteInfNanInU16x4.map(e => ({
input: u16x4ToVec4F16(e),
expected: bitcastVec4F16ToVec2U32Comparator(e),
})),
vec4_f16_to_vec2_i32: () =>
f16Vec2FiniteInU16x4.map(e => ({
input: u16x4ToVec4F16(e),
expected: bitcastVec4F16ToVec2I32Comparator(e),
})),
vec4_f16_inf_nan_to_vec2_i32: () =>
f16Vec2FiniteInfNanInU16x4.map(e => ({
input: u16x4ToVec4F16(e),
expected: bitcastVec4F16ToVec2I32Comparator(e),
})),
vec4_f16_to_vec2_f32_finite: () =>
f16Vec2FiniteInU16x4
.filter(
u16x4 =>
isFiniteF32(reinterpretU32AsF32(u16x2ToU32(u16x4.slice(0, 2)))) &&
isFiniteF32(reinterpretU32AsF32(u16x2ToU32(u16x4.slice(2, 4))))
)
.map(e => ({
input: u16x4ToVec4F16(e),
expected: bitcastVec4F16ToVec2F32Comparator(e),
})),
vec4_f16_inf_nan_to_vec2_f32: () =>
f16Vec2FiniteInfNanInU16x4.map(e => ({
input: u16x4ToVec4F16(e),
expected: bitcastVec4F16ToVec2F32Comparator(e),
})),
});