diff --git a/js/common/lib/env.ts b/js/common/lib/env.ts index d6d9f7fa48790..112c8a1f78851 100644 --- a/js/common/lib/env.ts +++ b/js/common/lib/env.ts @@ -41,16 +41,22 @@ export declare namespace Env { numThreads?: number; /** - * set or get a boolean value indicating whether to enable SIMD. If set to false, SIMD will be forcely disabled. + * set a value indicating whether to enable SIMD. + * + * ONNX Runtime will perform feature detection based on the value of this property. Specifically, when the value is + * set to: + * - `undefined`, `true` or `"fixed"`: will check availability of Fixed-width SIMD. + * - `"relaxed"`: will check availability of Relaxed SIMD. + * - `false`: will not perform SIMD feature checking. + * + * Setting this property does not make ONNX Runtime to switch to the corresponding runtime automatically. User need + * to set `wasmPaths` or `wasmBinary` property to load the corresponding runtime. * * This setting is available only when WebAssembly SIMD feature is available in current context. * * @defaultValue `true` - * - * @deprecated This property is deprecated. Since SIMD is supported by all major JavaScript engines, non-SIMD - * build is no longer provided. This property will be removed in future release. */ - simd?: boolean; + simd?: boolean | 'fixed' | 'relaxed'; /** * set or get a boolean value indicating whether to enable trace. diff --git a/js/web/lib/backend-wasm.ts b/js/web/lib/backend-wasm.ts index 5e5f4804aed92..fe8bfaa188ea9 100644 --- a/js/web/lib/backend-wasm.ts +++ b/js/web/lib/backend-wasm.ts @@ -17,12 +17,13 @@ export const initializeFlags = (): void => { env.wasm.initTimeout = 0; } - if (env.wasm.simd === false) { + const simd = env.wasm.simd; + if (typeof simd !== 'boolean' && simd !== undefined && simd !== 'fixed' && simd !== 'relaxed') { // eslint-disable-next-line no-console console.warn( - 'Deprecated property "env.wasm.simd" is set to false. ' + - 'non-SIMD build is no longer provided, and this setting will be ignored.', + `Property "env.wasm.simd" is set to unknown value "${simd}". Reset it to \`false\` and ignore SIMD feature checking.`, ); + env.wasm.simd = false; } if (typeof env.wasm.proxy !== 'boolean') { diff --git a/js/web/lib/wasm/wasm-factory.ts b/js/web/lib/wasm/wasm-factory.ts index 0f49d25040409..23eb2f0978feb 100644 --- a/js/web/lib/wasm/wasm-factory.ts +++ b/js/web/lib/wasm/wasm-factory.ts @@ -64,6 +64,34 @@ const isSimdSupported = (): boolean => { } }; +const isRelaxedSimdSupported = (): boolean => { + try { + // Test for WebAssembly Relaxed SIMD capability (for both browsers and Node.js) + // This typed array is a WebAssembly program containing Relaxed SIMD instructions. + + // The binary data is generated from the following code by wat2wasm: + // (module + // (func (result v128) + // i32.const 1 + // i8x16.splat + // i32.const 2 + // i8x16.splat + // i32.const 3 + // i8x16.splat + // i32x4.relaxed_dot_i8x16_i7x16_add_s + // ) + // ) + return WebAssembly.validate( + new Uint8Array([ + 0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 123, 3, 2, 1, 0, 10, 19, 1, 17, 0, 65, 1, 253, 15, 65, 2, 253, + 15, 65, 3, 253, 15, 253, 147, 2, 11, + ]), + ); + } catch (e) { + return false; + } +}; + export const initializeWebAssembly = async (flags: Env.WebAssemblyFlags): Promise => { if (initialized) { return Promise.resolve(); @@ -82,7 +110,14 @@ export const initializeWebAssembly = async (flags: Env.WebAssemblyFlags): Promis let numThreads = flags.numThreads!; // ensure SIMD is supported - if (!isSimdSupported()) { + if (flags.simd === false) { + // skip SIMD feature checking as it is disabled explicitly by user + } else if (flags.simd === 'relaxed') { + // check if relaxed SIMD is supported + if (!isRelaxedSimdSupported()) { + throw new Error('Relaxed WebAssembly SIMD is not supported in the current environment.'); + } + } else if (!isSimdSupported()) { throw new Error('WebAssembly SIMD is not supported in the current environment.'); }