From 2be0e90f782b2693f964e59d38044f0a1dd65ccb Mon Sep 17 00:00:00 2001 From: Austin Eng Date: Wed, 25 Oct 2023 13:06:10 -0700 Subject: [PATCH] Remove duplicate definitions of reinterpret* (#3095) --- src/unittests/floating_point.spec.ts | 7 +- src/unittests/maths.spec.ts | 8 +- .../expression/call/builtin/bitcast.spec.ts | 18 +-- .../expression/unary/af_assignment.spec.ts | 8 +- .../expression/unary/i32_conversion.spec.ts | 2 +- .../expression/unary/u32_conversion.spec.ts | 2 +- src/webgpu/util/constants.ts | 50 +------- src/webgpu/util/conversion.ts | 85 ------------- src/webgpu/util/floating_point.ts | 14 ++- src/webgpu/util/math.ts | 40 ++---- src/webgpu/util/reinterpret.ts | 118 ++++++++++++++++++ 11 files changed, 160 insertions(+), 192 deletions(-) create mode 100644 src/webgpu/util/reinterpret.ts diff --git a/src/unittests/floating_point.spec.ts b/src/unittests/floating_point.spec.ts index da9110bc2d70..d016980a13f1 100644 --- a/src/unittests/floating_point.spec.ts +++ b/src/unittests/floating_point.spec.ts @@ -6,15 +6,12 @@ import { makeTestGroup } from '../common/framework/test_group.js'; import { objectEquals, unreachable } from '../common/util/util.js'; import { kValue } from '../webgpu/util/constants.js'; import { FP, FPInterval, FPIntervalParam, IntervalBounds } from '../webgpu/util/floating_point.js'; +import { map2DArray, oneULPF32, oneULPF16, oneULPF64 } from '../webgpu/util/math.js'; import { reinterpretU16AsF16, reinterpretU32AsF32, reinterpretU64AsF64, - map2DArray, - oneULPF32, - oneULPF16, - oneULPF64, -} from '../webgpu/util/math.js'; +} from '../webgpu/util/reinterpret.js'; import { UnitTest } from './unit_test.js'; diff --git a/src/unittests/maths.spec.ts b/src/unittests/maths.spec.ts index 126a0d371e03..1c37e436fbd5 100644 --- a/src/unittests/maths.spec.ts +++ b/src/unittests/maths.spec.ts @@ -25,9 +25,6 @@ import { fullF16Range, fullF32Range, fullI32Range, - reinterpretU16AsF16, - reinterpretU32AsF32, - reinterpretU64AsF64, lerp, linearRange, nextAfterF16, @@ -40,6 +37,11 @@ import { lerpBigInt, linearRangeBigInt, } from '../webgpu/util/math.js'; +import { + reinterpretU16AsF16, + reinterpretU32AsF32, + reinterpretU64AsF64, +} from '../webgpu/util/reinterpret.js'; import { UnitTest } from './unit_test.js'; diff --git a/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts index 9f0938f430c6..7f84f016683b 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts @@ -25,14 +25,6 @@ import { GPUTest } from '../../../../../gpu_test.js'; import { Comparator, alwaysPass, anyOf } from '../../../../../util/compare.js'; import { kBit, kValue } from '../../../../../util/constants.js'; import { - reinterpretI32AsF32, - reinterpretI32AsU32, - reinterpretF32AsI32, - reinterpretF32AsU32, - reinterpretU32AsF32, - reinterpretU32AsI32, - reinterpretU16AsF16, - reinterpretF16AsU16, f32, i32, u32, @@ -59,6 +51,16 @@ import { isFiniteF32, isFiniteF16, } from '../../../../../util/math.js'; +import { + reinterpretI32AsF32, + reinterpretI32AsU32, + reinterpretF32AsI32, + reinterpretF32AsU32, + reinterpretU32AsF32, + reinterpretU32AsI32, + reinterpretU16AsF16, + reinterpretF16AsU16, +} from '../../../../../util/reinterpret.js'; import { makeCaseCache } from '../../case_cache.js'; import { allInputSources, run, ShaderBuilder } from '../../expression.js'; diff --git a/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts b/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts index 372051c949a3..141d87d0f2d9 100644 --- a/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts @@ -7,12 +7,8 @@ import { GPUTest } from '../../../../gpu_test.js'; import { kValue } from '../../../../util/constants.js'; import { abstractFloat, TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../util/conversion.js'; import { FP } from '../../../../util/floating_point.js'; -import { - filteredF64Range, - fullF64Range, - isSubnormalNumberF64, - reinterpretU64AsF64, -} from '../../../../util/math.js'; +import { filteredF64Range, fullF64Range, isSubnormalNumberF64 } from '../../../../util/math.js'; +import { reinterpretU64AsF64 } from '../../../../util/reinterpret.js'; import { makeCaseCache } from '../case_cache.js'; import { abstractFloatShaderBuilder, diff --git a/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts index 4d326f87f776..a77aa0e4d345 100644 --- a/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts @@ -10,7 +10,6 @@ import { f32, f16, i32, - reinterpretU32AsI32, TypeBool, TypeF32, TypeF16, @@ -26,6 +25,7 @@ import { quantizeToF32, quantizeToF16, } from '../../../../util/math.js'; +import { reinterpretU32AsI32 } from '../../../../util/reinterpret.js'; import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run, ShaderBuilder } from '../expression.js'; diff --git a/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts index 255662c6e402..87dc6e7a5df8 100644 --- a/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts @@ -10,7 +10,6 @@ import { f32, f16, i32, - reinterpretI32AsU32, TypeBool, TypeF32, TypeF16, @@ -26,6 +25,7 @@ import { quantizeToF32, quantizeToF16, } from '../../../../util/math.js'; +import { reinterpretI32AsU32 } from '../../../../util/reinterpret.js'; import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run, ShaderBuilder } from '../expression.js'; diff --git a/src/webgpu/util/constants.ts b/src/webgpu/util/constants.ts index 0dec73f187c1..5ee819c64e4f 100644 --- a/src/webgpu/util/constants.ts +++ b/src/webgpu/util/constants.ts @@ -1,4 +1,9 @@ -import { Float16Array } from '../../external/petamoriken/float16/float16.js'; +import { + reinterpretU64AsF64, + reinterpretF64AsU64, + reinterpretU32AsF32, + reinterpretU16AsF16, +} from './reinterpret.js'; export const kBit = { // Limits of int32 @@ -236,49 +241,6 @@ export const kBit = { }, } as const; -/** - * @returns a 64-bit float value via interpreting the input as the bit - * representation as a 64-bit integer - * - * Using a locally defined function here to avoid compile time dependency - * issues. - */ -function reinterpretU64AsF64(input: bigint): number { - return new Float64Array(new BigUint64Array([input]).buffer)[0]; -} - -/** - * @returns the 64-bit integer bit representation of 64-bit float value - * - * Using a locally defined function here to avoid compile time dependency - * issues. - */ -function reinterpretF64AsU64(input: number): bigint { - return new BigUint64Array(new Float64Array([input]).buffer)[0]; -} - -/** - * @returns a 32-bit float value via interpreting the input as the bit - * representation as a 32-bit integer - * - * Using a locally defined function here to avoid compile time dependency - * issues. - */ -function reinterpretU32AsF32(input: number): number { - return new Float32Array(new Uint32Array([input]).buffer)[0]; -} - -/** - * @returns a 16-bit float value via interpreting the input as the bit - * representation as a 64-bit integer - * - * Using a locally defined function here to avoid compile time dependency - * issues. - */ -function reinterpretU16AsF16(input: number): number { - return new Float16Array(new Uint16Array([input]).buffer)[0]; -} - export const kValue = { // Limits of i32 i32: { diff --git a/src/webgpu/util/conversion.ts b/src/webgpu/util/conversion.ts index 5ee3e30c36ac..f94c2b918806 100644 --- a/src/webgpu/util/conversion.ts +++ b/src/webgpu/util/conversion.ts @@ -1094,91 +1094,6 @@ export const True = bool(true); /** A 'false' literal value */ export const False = bool(false); -// Encoding to u32s, instead of BigInt, for serialization -export function reinterpretF64AsU32s(f64: number): [number, number] { - workingDataF64[0] = f64; - return [workingDataU32[0], workingDataU32[1]]; -} - -// De-encoding from u32s, instead of BigInt, for serialization -export function reinterpretU32sAsF64(u32s: [number, number]): number { - workingDataU32[0] = u32s[0]; - workingDataU32[1] = u32s[1]; - return workingDataF64[0]; -} - -/** - * @returns a number representing the u32 interpretation - * of the bits of a number assumed to be an f32 value. - */ -export function reinterpretF32AsU32(f32: number): number { - workingDataF32[0] = f32; - return workingDataU32[0]; -} - -/** - * @returns a number representing the i32 interpretation - * of the bits of a number assumed to be an f32 value. - */ -export function reinterpretF32AsI32(f32: number): number { - workingDataF32[0] = f32; - return workingDataI32[0]; -} - -/** - * @returns a number representing the f32 interpretation - * of the bits of a number assumed to be an u32 value. - */ -export function reinterpretU32AsF32(u32: number): number { - workingDataU32[0] = u32; - return workingDataF32[0]; -} - -/** - * @returns a number representing the i32 interpretation - * of the bits of a number assumed to be an u32 value. - */ -export function reinterpretU32AsI32(u32: number): number { - workingDataU32[0] = u32; - return workingDataI32[0]; -} - -/** - * @returns a number representing the u32 interpretation - * of the bits of a number assumed to be an i32 value. - */ -export function reinterpretI32AsU32(i32: number): number { - workingDataI32[0] = i32; - return workingDataU32[0]; -} - -/** - * @returns a number representing the f32 interpretation - * of the bits of a number assumed to be an i32 value. - */ -export function reinterpretI32AsF32(i32: number): number { - workingDataI32[0] = i32; - return workingDataF32[0]; -} - -/** - * @returns a number representing the u16 interpretation - * of the bits of a number assumed to be an f16 value. - */ -export function reinterpretF16AsU16(f16: number): number { - workingDataF16[0] = f16; - return workingDataU16[0]; -} - -/** - * @returns a number representing the f16 interpretation - * of the bits of a number assumed to be an u16 value. - */ -export function reinterpretU16AsF16(u16: number): number { - workingDataU16[0] = u16; - return workingDataF16[0]; -} - /** * Class that encapsulates a vector value. */ diff --git a/src/webgpu/util/floating_point.ts b/src/webgpu/util/floating_point.ts index fc76f85260fa..70109f5cab9e 100644 --- a/src/webgpu/util/floating_point.ts +++ b/src/webgpu/util/floating_point.ts @@ -9,12 +9,6 @@ import { f16, f32, isFloatType, - reinterpretF16AsU16, - reinterpretF32AsU32, - reinterpretF64AsU32s, - reinterpretU16AsF16, - reinterpretU32AsF32, - reinterpretU32sAsF64, Scalar, ScalarType, toMatrix, @@ -45,6 +39,14 @@ import { unflatten2DArray, every2DArray, } from './math.js'; +import { + reinterpretF16AsU16, + reinterpretF32AsU32, + reinterpretF64AsU32s, + reinterpretU16AsF16, + reinterpretU32AsF32, + reinterpretU32sAsF64, +} from './reinterpret.js'; /** Indicate the kind of WGSL floating point numbers being operated on */ export type FPKind = 'f32' | 'f16' | 'abstract'; diff --git a/src/webgpu/util/math.ts b/src/webgpu/util/math.ts index 1fb1d1a7ff69..c22b4f395024 100644 --- a/src/webgpu/util/math.ts +++ b/src/webgpu/util/math.ts @@ -6,7 +6,13 @@ import { } from '../../external/petamoriken/float16/float16.js'; import { kBit, kValue } from './constants.js'; -import { floatBitsToNumber, i32, kFloat16Format, kFloat32Format, u32 } from './conversion.js'; +import { i32, u32 } from './conversion.js'; +import { + reinterpretF64AsU64, + reinterpretU64AsF64, + reinterpretU32AsF32, + reinterpretU16AsF16, +} from './reinterpret.js'; /** * A multiple of 8 guaranteed to be way too large to allocate (just under 8 pebibytes). @@ -2061,38 +2067,6 @@ export function lcm(a: number, b: number): number { return (a * b) / gcd(a, b); } -/** - * @returns the bit representation as a 64-integer, via interpreting the input - * as a 64-bit float value - */ -export function reinterpretF64AsU64(input: number): bigint { - return new BigUint64Array(new Float64Array([input]).buffer)[0]; -} - -/** - * @returns a 64-bit float value via interpreting the input as the bit - * representation as a 64-bit integer - */ -export function reinterpretU64AsF64(input: bigint): number { - return new Float64Array(new BigUint64Array([input]).buffer)[0]; -} - -/** - * @returns a 32-bit float value via interpreting the input as the bit - * representation as a 32-bit integer - */ -export function reinterpretU32AsF32(input: number): number { - return floatBitsToNumber(input, kFloat32Format); -} - -/** - * @returns a 16-bit float value via interpreting the input as the bit - * representation as a 16-bit integer - */ -export function reinterpretU16AsF16(hex: number): number { - return floatBitsToNumber(hex, kFloat16Format); -} - /** @returns the cross of an array with the intermediate result of cartesianProduct * * @param elements array of values to cross with the intermediate result of diff --git a/src/webgpu/util/reinterpret.ts b/src/webgpu/util/reinterpret.ts new file mode 100644 index 000000000000..2ffb24b231f0 --- /dev/null +++ b/src/webgpu/util/reinterpret.ts @@ -0,0 +1,118 @@ +import { Float16Array } from '../../external/petamoriken/float16/float16.js'; + +/** + * Once-allocated ArrayBuffer/views to avoid overhead of allocation when converting between numeric formats + * + * workingData* is shared between multiple functions in this file, so to avoid re-entrancy problems, make sure in + * functions that use it that they don't call themselves or other functions that use workingData*. + */ +const workingData = new ArrayBuffer(8); +const workingDataU32 = new Uint32Array(workingData); +const workingDataU16 = new Uint16Array(workingData); +const workingDataF32 = new Float32Array(workingData); +const workingDataF16 = new Float16Array(workingData); +const workingDataI32 = new Int32Array(workingData); +const workingDataF64 = new Float64Array(workingData); +const workingDataU64 = new BigUint64Array(workingData); + +/** + * @returns a 64-bit float value via interpreting the input as the bit + * representation as a 64-bit integer + */ +export function reinterpretU64AsF64(input: bigint): number { + workingDataU64[0] = input; + return workingDataF64[0]; +} + +/** + * @returns the 64-bit integer bit representation of 64-bit float value + */ +export function reinterpretF64AsU64(input: number): bigint { + workingDataF64[0] = input; + return workingDataU64[0]; +} + +// Encoding to u32s, instead of BigInt, for serialization +export function reinterpretF64AsU32s(f64: number): [number, number] { + workingDataF64[0] = f64; + return [workingDataU32[0], workingDataU32[1]]; +} + +// De-encoding from u32s, instead of BigInt, for serialization +export function reinterpretU32sAsF64(u32s: [number, number]): number { + workingDataU32[0] = u32s[0]; + workingDataU32[1] = u32s[1]; + return workingDataF64[0]; +} + +/** + * @returns a number representing the u32 interpretation + * of the bits of a number assumed to be an f32 value. + */ +export function reinterpretF32AsU32(f32: number): number { + workingDataF32[0] = f32; + return workingDataU32[0]; +} + +/** + * @returns a number representing the i32 interpretation + * of the bits of a number assumed to be an f32 value. + */ +export function reinterpretF32AsI32(f32: number): number { + workingDataF32[0] = f32; + return workingDataI32[0]; +} + +/** + * @returns a number representing the f32 interpretation + * of the bits of a number assumed to be an u32 value. + */ +export function reinterpretU32AsF32(u32: number): number { + workingDataU32[0] = u32; + return workingDataF32[0]; +} + +/** + * @returns a number representing the i32 interpretation + * of the bits of a number assumed to be an u32 value. + */ +export function reinterpretU32AsI32(u32: number): number { + workingDataU32[0] = u32; + return workingDataI32[0]; +} + +/** + * @returns a number representing the u32 interpretation + * of the bits of a number assumed to be an i32 value. + */ +export function reinterpretI32AsU32(i32: number): number { + workingDataI32[0] = i32; + return workingDataU32[0]; +} + +/** + * @returns a number representing the f32 interpretation + * of the bits of a number assumed to be an i32 value. + */ +export function reinterpretI32AsF32(i32: number): number { + workingDataI32[0] = i32; + return workingDataF32[0]; +} + +/** + * @returns a number representing the u16 interpretation + * of the bits of a number assumed to be an f16 value. + */ +export function reinterpretF16AsU16(f16: number): number { + workingDataF16[0] = f16; + return workingDataU16[0]; +} + +/** + * @returns a number representing the f16 interpretation + * of the bits of a number assumed to be an u16 value. + */ +export function reinterpretU16AsF16(u16: number): number { + workingDataU16[0] = u16; + return workingDataF16[0]; +}