Emscripten 支援 WebAssembly SIMD 功能。在您的 C/C++ 程式中,有五種不同的方法可以運用 WebAssembly SIMD
啟用 LLVM/Clang SIMD 自動向量化器,自動以 WebAssembly SIMD 為目標,無需變更 C/C++ 原始碼。
使用 GCC/Clang SIMD 向量擴充功能 (__attribute__((vector_size(16)))
) 撰寫 SIMD 程式碼
使用 WebAssembly SIMD 內建函式 (#include <wasm_simd128.h>
) 撰寫 SIMD 程式碼
編譯使用 x86 SSE、SSE2、SSE3、SSSE3、SSE4.1、SSE4.2 或 AVX 內建函式 (#include <*mmintrin.h>
) 的現有 SIMD 程式碼
編譯使用 ARM NEON 內建函式 (#include <arm_neon.h>
) 的現有 SIMD 程式碼
這些技巧可以自由地在單一程式中組合使用。
若要啟用上述五種類型的任何 SIMD,請在編譯時傳遞 WebAssembly 專用的 -msimd128
旗標。這也會開啟 LLVM 的自動向量化過程。如果這並非所要的,請另外傳遞旗標 -fno-vectorize -fno-slp-vectorize
來停用自動向量化器。如需詳細資訊,請參閱 LLVM 中的自動向量化。
下列項目支援 WebAssembly SIMD:
Chrome ≥ 91 (2021 年 5 月),
Firefox ≥ 89 (2021 年 6 月),
Safari ≥ 16.4 (2023 年 3 月) 和
Node.js ≥ 16.4 (2021 年 6 月)。
如需其他 VM 的詳細資訊,請參閱 WebAssembly 藍圖。
即將推出的 寬鬆 SIMD 提案將會為 WebAssembly 新增更多 SIMD 指令。
在原始碼層級,可以使用 GCC/Clang SIMD 向量擴充功能,並會在可能的情況下將其降級為 WebAssembly SIMD 指令。
這讓開發人員能夠透過 typedef 建立自訂的寬向量類型,並在向量化類型上使用算術運算子 (+、-、*、/),也能夠透過 vector[i] 符號存取個別通道。不過,GCC 向量內建函式無法使用。請改用下列 WebAssembly SIMD 內建函式。
LLVM 維護一個 WebAssembly SIMD 內建函式標頭檔,該標頭檔隨 Emscripten 一起提供,並為不同支援的向量類型新增類型定義。
#include <wasm_simd128.h> #include <stdio.h> int main() { #ifdef __wasm_simd128__ v128_t v1 = wasm_f32x4_make(1.2f, 3.4f, 5.6f, 7.8f); v128_t v2 = wasm_f32x4_make(2.1f, 4.3f, 6.5f, 8.7f); v128_t v3 = wasm_f32x4_add(v1, v2); // Prints "v3: [3.3, 7.7, 12.1, 16.5]" printf("v3: [%.1f, %.1f, %.1f, %.1f]\n", wasm_f32x4_extract_lane(v3, 0), wasm_f32x4_extract_lane(v3, 1), wasm_f32x4_extract_lane(v3, 2), wasm_f32x4_extract_lane(v3, 3)); #endif }
您可以在 wasm_simd128.h 線上瀏覽 Wasm SIMD 標頭。
在編譯時傳遞旗標 -msimd128
,即可啟用以 WebAssembly SIMD 內建函式為目標。C/C++ 程式碼可以使用內建前置處理器定義 #ifdef __wasm_simd128__
來偵測是否已啟用 WebAssembly SIMD 的建置。
傳遞 -mrelaxed-simd
以將 WebAssembly 寬鬆 SIMD 內建函式設為目標。C/C++ 程式碼可以使用內建前置處理器定義 #ifdef __wasm_relaxed_simd__
來偵測這個目標是否作用中。
在移植原生 SIMD 程式碼時,應注意由於可攜性考量,WebAssembly SIMD 規格不會公開存取所有原生 x86/ARM SIMD 指令。特別是,存在下列變更
Emscripten 不支援 x86 或任何其他原生內嵌 SIMD 組譯,或建置 .s 組譯檔案,因此所有程式碼都應撰寫為使用 SIMD 內建函式或編譯器向量擴充功能。
WebAssembly SIMD 無法控制管理浮點捨入模式或處理非正規值。
快取行預取指令無法使用,而且對這些函式的呼叫將會編譯,但會被視為無運算。
非對稱記憶體屏障運算無法使用,但在啟用 SharedArrayBuffer (-pthread) 時會實作為完全同步的記憶體屏障,或者在未啟用多執行緒時會實作為無運算 (預設值)。
SIMD 相關的錯誤回報會在使用標籤 SIMD 的 Emscripten 錯誤追蹤器中追蹤。
當開發 SIMD 程式碼以使用 WebAssembly SIMD 時,實作者應注意主機硬體與 WebAssembly 語意之間的語意差異;如 WebAssembly 設計文件中所述,「這有時會導致效能不佳」。下列清單概述在效能調整時應注意的一些 WebAssembly SIMD 指令
WebAssembly SIMD 指令 |
架構 |
考量事項 |
---|---|---|
[i8x16|i16x8|i32x4|i64x2].[shl|shr_s|shr_u] |
x86、arm |
使用常數移位量來避免額外的指令檢查其是否在界限內。 |
i8x16.[shl|shr_s|shr_u] |
x86 |
包含用於正交性,這些指令沒有對等的 x86 指令,並使用 v8 中的 5-11 個 x86 指令模擬 (即使用 16x8 移位)。 |
i64x2.shr_s |
x86 |
包含用於正交性,此指令沒有對等的 x86 指令,並使用 v8 中的 6-12 個 x86 指令模擬。 |
i8x16.swizzle |
x86 |
歸零行為與 x86 不符 (即,當索引超出範圍時,此指令會歸零,而不是當最高有效位元為 1 時);使用常數調整量 (或 i8x16.shuffle) 以避免某些執行階段中出現 3 個額外的 x86 指令。 |
[f32x4|f64x2].[min|max] |
x86 |
與純量版本相同,NaN 傳播語意會強制執行階段以 7-10 個 x86 指令模擬 (例如,請參閱 v8 的模擬);如果可能,請改用 [f32x4|f64x2].[pmin|pmax] (1 個 x86 指令)。 |
i32x4.trunc_sat_f32x4_[u|s] |
x86 |
沒有對等的 x86 語意;以 v8 中的 8-14 個 x86 指令模擬。 |
i32x4.trunc_sat_f64x2_[u|s]_zero |
x86 |
沒有對等的 x86 語意;以 v8 中的 5-6 個 x86 指令模擬。 |
f32x4.convert_f32x4_u |
x86 |
沒有對等的 x86 語義;在 v8 中使用 8 個 x86 指令模擬。 |
[i8x16|i64x2].mul |
x86 |
包含這些指令是為了正交性,這些指令沒有對等的 x86 指令,並且在 v8 中使用 10 個 x86 指令模擬。 |
Emscripten 支援透過傳遞 -msimd128
旗標,以及以下其中之一來編譯使用 x86 SSE 指令的現有程式碼庫
SSE:傳遞 -msse
和 #include <xmmintrin.h>
。使用 #ifdef __SSE__
來閘控程式碼。
SSE2:傳遞 -msse2
和 #include <emmintrin.h>
。使用 #ifdef __SSE2__
來閘控程式碼。
SSE3:傳遞 -msse3
和 #include <pmmintrin.h>
。使用 #ifdef __SSE3__
來閘控程式碼。
SSSE3:傳遞 -mssse3
和 #include <tmmintrin.h>
。使用 #ifdef __SSSE3__
來閘控程式碼。
SSE4.1:傳遞 -msse4.1
和 #include <smmintrin.h>
。使用 #ifdef __SSE4_1__
來閘控程式碼。
SSE4.2:傳遞 -msse4.2
和 #include <nmmintrin.h>
。使用 #ifdef __SSE4_2__
來閘控程式碼。
AVX:傳遞 -mavx
和 #include <immintrin.h>
。使用 #ifdef __AVX__
來閘控程式碼。
目前僅支援 SSE1、SSE2、SSE3、SSSE3、SSE4.1、SSE4.2 和 AVX 指令集。這些指令集每一個都是在前一個的基礎上新增的,因此例如,當以 SSE3 為目標時,SSE1 和 SSE2 指令集也可用。
下表重點說明了不同 SSE* 內建函數的可用性和預期效能。這對於理解 Wasm SIMD 規範在 x86 硬體上執行時的效能限制很有用。
有關每個 SSE 內建函數的詳細資訊,請造訪出色的Intel Intrinsics Guide on SSE1。
✅ Wasm SIMD 有一個符合 x86 SSE 指令的原生運算碼,應該產生原生效能
💡 雖然 Wasm SIMD 規範沒有提供適當的效能保證,但考慮到有足夠聰明的編譯器和執行階段 VM 路徑,此內建函數應該能夠產生相同的原生 SSE 指令。
🟡 有些資訊遺失(例如類型或對齊資訊),因此無法保證 Wasm VM 能夠重建預期的 x86 SSE 運算碼。這可能會導致效能損失,具體取決於目標 CPU 硬體系列,尤其是在較舊的 CPU 世代上。
⚠️ 底層的 x86 SSE 指令不可用,但它最多透過其他幾個 Wasm SIMD 指令模擬,導致輕微的效能損失。
❌ Wasm SIMD 規範未公開底層的 x86 SSE 指令,因此必須透過慢速路徑模擬,例如一系列較慢的 SIMD 指令,或純量實作。
💣 Wasm SIMD 中沒有底層的 x86 SSE 運算碼,並且實作必須使用如此慢的模擬路徑,因此建議在更高層級重新思考演算法的解決方案。
💭 提供的 SSE 內建函數可讓應用程式編譯,但不執行任何操作。
⚫ 提供的 SSE 內建函數不可用。引用內建函數會導致編譯器錯誤。
下表中的某些內建函數標記為「虛擬」。這表示實際上不存在用於實作它們的原生 x86 SSE 指令集運算碼,但原生編譯器會提供該函數作為便利功能。不同的編譯器可能會為這些指令產生不同的指令序列。
除了查閱下表外,您還可以透過定義巨集 #define WASM_SIMD_COMPAT_SLOW
來開啟慢速、模擬函數的診斷。如果您嘗試使用任何慢速路徑(對應於圖例中的 ❌ 或 💣),這將會列印出警告。
內建函數名稱 |
WebAssembly SIMD 支援 |
---|---|
_mm_set_ps |
✅ wasm_f32x4_make |
_mm_setr_ps |
✅ wasm_f32x4_make |
_mm_set_ss |
💡 使用 wasm_f32x4_make 模擬 |
_mm_set_ps1 (_mm_set1_ps) |
✅ wasm_f32x4_splat |
_mm_setzero_ps |
💡 使用 wasm_f32x4_const(0) 模擬 |
_mm_load_ps |
🟡 wasm_v128_load。VM 必須猜測類型。 |
_mm_loadl_pi |
❌ 沒有 Wasm SIMD 支援。 |
_mm_loadh_pi |
❌ 沒有 Wasm SIMD 支援。 |
_mm_loadr_ps |
💡 虛擬。Simd 載入 + 混洗。 |
_mm_loadu_ps |
🟡 wasm_v128_load。VM 必須猜測類型。 |
_mm_load_ps1 (_mm_load1_ps) |
🟡 虛擬。Simd 載入 + 混洗。 |
_mm_load_ss |
❌ 使用 wasm_f32x4_make 模擬 |
_mm_storel_pi |
❌ 純量儲存 |
_mm_storeh_pi |
❌ 混洗 + 純量儲存 |
_mm_store_ps |
🟡 wasm_v128_store。VM 必須猜測類型。 |
_mm_stream_ps |
🟡 wasm_v128_store。VM 必須猜測類型。 |
_mm_prefetch |
💭 無操作。 |
_mm_sfence |
⚠️ 在多執行緒建置中,會完全阻礙。 |
_mm_shuffle_ps |
🟡 wasm_i32x4_shuffle。VM 必須猜測類型。 |
_mm_storer_ps |
💡 虛擬。混洗 + Simd 儲存。 |
_mm_store_ps1 (_mm_store1_ps) |
🟡 虛擬。使用混洗模擬。 |
_mm_store_ss |
💡 使用純量儲存模擬 |
_mm_storeu_ps |
🟡 wasm_v128_store。VM 必須猜測類型。 |
_mm_storeu_si16 |
💡 使用純量儲存模擬 |
_mm_storeu_si64 |
💡 使用純量儲存模擬 |
_mm_movemask_ps |
✅ wasm_i32x4_bitmask |
_mm_move_ss |
💡 使用混洗模擬。VM 必須猜測類型。 |
_mm_add_ps |
✅ wasm_f32x4_add |
_mm_add_ss |
⚠️ 使用混洗模擬 |
_mm_sub_ps |
✅ wasm_f32x4_sub |
_mm_sub_ss |
⚠️ 使用混洗模擬 |
_mm_mul_ps |
✅ wasm_f32x4_mul |
_mm_mul_ss |
⚠️ 使用混洗模擬 |
_mm_div_ps |
✅ wasm_f32x4_div |
_mm_div_ss |
⚠️ 使用混洗模擬 |
_mm_min_ps |
TODO:pmin 一旦它起作用 |
_mm_min_ss |
⚠️ 使用混洗模擬 |
_mm_max_ps |
TODO:pmax 一旦它起作用 |
_mm_max_ss |
⚠️ 使用混洗模擬 |
_mm_rcp_ps |
❌ 沒有 Wasm SIMD 支援。 |
_mm_rcp_ss |
❌ 沒有 Wasm SIMD 支援。 |
_mm_sqrt_ps |
✅ wasm_f32x4_sqrt |
_mm_sqrt_ss |
⚠️ 使用混洗模擬 |
_mm_rsqrt_ps |
❌ 沒有 Wasm SIMD 支援。 |
_mm_rsqrt_ss |
❌ 沒有 Wasm SIMD 支援。 |
_mm_unpackhi_ps |
💡 使用混洗模擬 |
_mm_unpacklo_ps |
💡 使用混洗模擬 |
_mm_movehl_ps |
💡 使用混洗模擬 |
_mm_movelh_ps |
💡 使用混洗模擬 |
_MM_TRANSPOSE4_PS |
💡 使用混洗模擬 |
_mm_cmplt_ps |
✅ wasm_f32x4_lt |
_mm_cmplt_ss |
⚠️ 使用混洗模擬 |
_mm_cmple_ps |
✅ wasm_f32x4_le |
_mm_cmple_ss |
⚠️ 使用混洗模擬 |
_mm_cmpeq_ps |
✅ wasm_f32x4_eq |
_mm_cmpeq_ss |
⚠️ 使用混洗模擬 |
_mm_cmpge_ps |
✅ wasm_f32x4_ge |
_mm_cmpge_ss |
⚠️ 使用混洗模擬 |
_mm_cmpgt_ps |
✅ wasm_f32x4_gt |
_mm_cmpgt_ss |
⚠️ 使用混洗模擬 |
_mm_cmpord_ps |
❌ 使用 2xcmp+and 模擬 |
_mm_cmpord_ss |
❌ 使用 2xcmp+and+混洗模擬 |
_mm_cmpunord_ps |
❌ 使用 2xcmp+or 模擬 |
_mm_cmpunord_ss |
❌ 使用 2xcmp+or+混洗模擬 |
_mm_and_ps |
🟡 wasm_v128_and。VM 必須猜測類型。 |
_mm_andnot_ps |
🟡 wasm_v128_andnot。VM 必須猜測類型。 |
_mm_or_ps |
🟡 wasm_v128_or。VM 必須猜測類型。 |
_mm_xor_ps |
🟡 wasm_v128_xor。VM 必須猜測類型。 |
_mm_cmpneq_ps |
✅ wasm_f32x4_ne |
_mm_cmpneq_ss |
⚠️ 使用混洗模擬 |
_mm_cmpnge_ps |
⚠️ 使用 not+ge 模擬 |
_mm_cmpnge_ss |
⚠️ 使用 not+ge+混洗模擬 |
_mm_cmpngt_ps |
⚠️ 使用 not+gt 模擬 |
_mm_cmpngt_ss |
⚠️ 使用 not+gt+混洗模擬 |
_mm_cmpnle_ps |
⚠️ 使用 not+le 模擬 |
_mm_cmpnle_ss |
⚠️ 使用 not+le+混洗模擬 |
_mm_cmpnlt_ps |
⚠️ 使用 not+lt 模擬 |
_mm_cmpnlt_ss |
⚠️ 使用 not+lt+混洗模擬 |
_mm_comieq_ss |
❌ 純量化 |
_mm_comige_ss |
❌ 純量化 |
_mm_comigt_ss |
❌ 純量化 |
_mm_comile_ss |
❌ 純量化 |
_mm_comilt_ss |
❌ 純量化 |
_mm_comineq_ss |
❌ 純量化 |
_mm_ucomieq_ss |
❌ 純量化 |
_mm_ucomige_ss |
❌ 純量化 |
_mm_ucomigt_ss |
❌ 純量化 |
_mm_ucomile_ss |
❌ 純量化 |
_mm_ucomilt_ss |
❌ 純量化 |
_mm_ucomineq_ss |
❌ 純量化 |
_mm_cvtsi32_ss (_mm_cvt_si2ss) |
❌ 純量化 |
_mm_cvtss_si32 (_mm_cvt_ss2si) |
💣 具有複雜模擬語義的純量 |
_mm_cvttss_si32 (_mm_cvtt_ss2si) |
💣 具有複雜模擬語義的純量 |
_mm_cvtsi64_ss |
❌ 純量化 |
_mm_cvtss_si64 |
💣 具有複雜模擬語義的純量 |
_mm_cvttss_si64 |
💣 具有複雜模擬語義的純量 |
_mm_cvtss_f32 |
💡 純量取得 |
_mm_malloc |
✅ 以指定的對齊方式配置記憶體。 |
_mm_free |
✅ 別名為 free()。 |
_MM_GET_EXCEPTION_MASK |
✅ 始終返回遮罩所有例外狀況 (0x1f80)。 |
_MM_GET_EXCEPTION_STATE |
❌ 未追蹤例外狀況狀態。始終返回 0。 |
_MM_GET_FLUSH_ZERO_MODE |
✅ 始終返回 _MM_FLUSH_ZERO_OFF。 |
_MM_GET_ROUNDING_MODE |
✅ 始終返回 _MM_ROUND_NEAREST。 |
_mm_getcsr |
✅ 始終返回 _MM_FLUSH_ZERO_OFF |
_MM_SET_EXCEPTION_MASK |
⚫ 不可用。固定為遮罩所有例外狀況。 |
_MM_SET_EXCEPTION_STATE |
⚫ 不可用。固定為零/清除狀態。 |
_MM_SET_FLUSH_ZERO_MODE |
⚫ 不可用。固定為 _MM_FLUSH_ZERO_OFF。 |
_MM_SET_ROUNDING_MODE |
⚫ 不可用。固定為 _MM_ROUND_NEAREST。 |
_mm_setcsr |
⚫ 不可用。 |
_mm_undefined_ps |
✅ 虛擬 |
_mm_avg_pu8、_mm_avg_pu16、_mm_cvt_pi2ps、_mm_cvt_ps2pi、_mm_cvt_pi16_ps、_mm_cvt_pi32_ps、_mm_cvt_pi32x2_ps、_mm_cvt_pi8_ps、_mm_cvt_ps_pi16、_mm_cvt_ps_pi32、_mm_cvt_ps_pi8、_mm_cvt_pu16_ps、_mm_cvt_pu8_ps、_mm_cvtt_ps2pi、_mm_cvtt_pi16_ps、_mm_cvttps_pi32、_mm_extract_pi16、_mm_insert_pi16、_mm_maskmove_si64、_m_maskmovq、_mm_max_pi16、_mm_max_pu8、_mm_min_pi16、_mm_min_pu8、_mm_movemask_pi8、_mm_mulhi_pu16、_m_pavgb、_m_pavgw、_m_pextrw、_m_pinsrw、_m_pmaxsw、_m_pmaxub、_m_pminsw、_m_pminub、_m_pmovmskb、_m_pmulhuw、_m_psadbw、_m_pshufw、_mm_sad_pu8、_mm_shuffle_pi16 和 _mm_stream_pi。
任何引用這些內建函數的程式碼都無法編譯。
下表重點說明了不同 SSE2 內建函數的可用性和預期效能。請參閱Intel Intrinsics Guide on SSE2。
內建函數名稱 |
WebAssembly SIMD 支援 |
---|---|
_mm_add_epi16 |
✅ wasm_i16x8_add |
_mm_add_epi32 |
✅ wasm_i32x4_add |
_mm_add_epi64 |
✅ wasm_i64x2_add |
_mm_add_epi8 |
✅ wasm_i8x16_add |
_mm_add_pd |
✅ wasm_f64x2_add |
_mm_add_sd |
⚠️ 使用混洗模擬 |
_mm_adds_epi16 |
✅ wasm_i16x8_add_sat |
_mm_adds_epi8 |
✅ wasm_i8x16_add_sat |
_mm_adds_epu16 |
✅ wasm_u16x8_add_sat |
_mm_adds_epu8 |
✅ wasm_u8x16_add_sat |
_mm_and_pd |
🟡 wasm_v128_and。VM 必須猜測類型。 |
_mm_and_si128 |
🟡 wasm_v128_and。VM 必須猜測類型。 |
_mm_andnot_pd |
🟡 wasm_v128_andnot。VM 必須猜測類型。 |
_mm_andnot_si128 |
🟡 wasm_v128_andnot。VM 必須猜測類型。 |
_mm_avg_epu16 |
✅ wasm_u16x8_avgr |
_mm_avg_epu8 |
✅ wasm_u8x16_avgr |
_mm_castpd_ps |
✅ no-op |
_mm_castpd_si128 |
✅ no-op |
_mm_castps_pd |
✅ no-op |
_mm_castps_si128 |
✅ no-op |
_mm_castsi128_pd |
✅ no-op |
_mm_castsi128_ps |
✅ no-op |
_mm_clflush |
💭 無操作。Wasm SIMD 中沒有快取提示。 |
_mm_cmpeq_epi16 |
✅ wasm_i16x8_eq |
_mm_cmpeq_epi32 |
✅ wasm_i32x4_eq |
_mm_cmpeq_epi8 |
✅ wasm_i8x16_eq |
_mm_cmpeq_pd |
✅ wasm_f64x2_eq |
_mm_cmpeq_sd |
⚠️ 使用混洗模擬 |
_mm_cmpge_pd |
✅ wasm_f64x2_ge |
_mm_cmpge_sd |
⚠️ 使用混洗模擬 |
_mm_cmpgt_epi16 |
✅ wasm_i16x8_gt |
_mm_cmpgt_epi32 |
✅ wasm_i32x4_gt |
_mm_cmpgt_epi8 |
✅ wasm_i8x16_gt |
_mm_cmpgt_pd |
✅ wasm_f64x2_gt |
_mm_cmpgt_sd |
⚠️ 使用混洗模擬 |
_mm_cmple_pd |
✅ wasm_f64x2_le |
_mm_cmple_sd |
⚠️ 使用混洗模擬 |
_mm_cmplt_epi16 |
✅ wasm_i16x8_lt |
_mm_cmplt_epi32 |
✅ wasm_i32x4_lt |
_mm_cmplt_epi8 |
✅ wasm_i8x16_lt |
_mm_cmplt_pd |
✅ wasm_f64x2_lt |
_mm_cmplt_sd |
⚠️ 使用混洗模擬 |
_mm_cmpneq_pd |
✅ wasm_f64x2_ne |
_mm_cmpneq_sd |
⚠️ 使用混洗模擬 |
_mm_cmpnge_pd |
⚠️ 使用 not+ge 模擬 |
_mm_cmpnge_sd |
⚠️ 使用 not+ge+混洗模擬 |
_mm_cmpngt_pd |
⚠️ 使用 not+gt 模擬 |
_mm_cmpngt_sd |
⚠️ 使用 not+gt+混洗模擬 |
_mm_cmpnle_pd |
⚠️ 使用 not+le 模擬 |
_mm_cmpnle_sd |
⚠️ 使用 not+le+混洗模擬 |
_mm_cmpnlt_pd |
⚠️ 使用 not+lt 模擬 |
_mm_cmpnlt_sd |
⚠️ 使用 not+lt+混洗模擬 |
_mm_cmpord_pd |
❌ 使用 2xcmp+and 模擬 |
_mm_cmpord_sd |
❌ 使用 2xcmp+and+混洗模擬 |
_mm_cmpunord_pd |
❌ 使用 2xcmp+or 模擬 |
_mm_cmpunord_sd |
❌ 使用 2xcmp+or+混洗模擬 |
_mm_comieq_sd |
❌ 純量化 |
_mm_comige_sd |
❌ 純量化 |
_mm_comigt_sd |
❌ 純量化 |
_mm_comile_sd |
❌ 純量化 |
_mm_comilt_sd |
❌ 純量化 |
_mm_comineq_sd |
❌ 純量化 |
_mm_cvtepi32_pd |
✅ wasm_f64x2_convert_low_i32x4 |
_mm_cvtepi32_ps |
✅ wasm_f32x4_convert_i32x4 |
_mm_cvtpd_epi32 |
❌ 純量化 |
_mm_cvtpd_ps |
✅ wasm_f32x4_demote_f64x2_zero |
_mm_cvtps_epi32 |
❌ 純量化 |
_mm_cvtps_pd |
✅ wasm_f64x2_promote_low_f32x4 |
_mm_cvtsd_f64 |
✅ wasm_f64x2_extract_lane |
_mm_cvtsd_si32 |
❌ 純量化 |
_mm_cvtsd_si64 |
❌ 純量化 |
_mm_cvtsd_si64x |
❌ 純量化 |
_mm_cvtsd_ss |
❌ 純量化 |
_mm_cvtsi128_si32 |
✅ wasm_i32x4_extract_lane |
_mm_cvtsi128_si64 (_mm_cvtsi128_si64x) |
✅ wasm_i64x2_extract_lane |
_mm_cvtsi32_sd |
❌ 純量化 |
_mm_cvtsi32_si128 |
💡 使用 wasm_i32x4_make 模擬 |
_mm_cvtsi64_sd (_mm_cvtsi64x_sd) |
❌ 純量化 |
_mm_cvtsi64_si128 (_mm_cvtsi64x_si128) |
💡 使用 wasm_i64x2_make 模擬 |
_mm_cvtss_sd |
❌ 純量化 |
_mm_cvttpd_epi32 |
❌ 純量化 |
_mm_cvttps_epi32 |
❌ 純量化 |
_mm_cvttsd_si32 |
❌ 純量化 |
_mm_cvttsd_si64 (_mm_cvttsd_si64x) |
❌ 純量化 |
_mm_div_pd |
✅ wasm_f64x2_div |
_mm_div_sd |
⚠️ 使用混洗模擬 |
_mm_extract_epi16 |
✅ wasm_u16x8_extract_lane |
_mm_insert_epi16 |
✅ wasm_i16x8_replace_lane |
_mm_lfence |
⚠️ 在多執行緒建置中,會完全阻礙。 |
_mm_load_pd |
🟡 wasm_v128_load。VM 必須猜測類型。 |
_mm_load1_pd (_mm_load_pd1) |
🟡 虛擬化。wasm_v64x2_load_splat,虛擬機器必須猜測類型。 |
_mm_load_sd |
❌ 使用 wasm_f64x2_make 模擬 |
_mm_load_si128 |
🟡 wasm_v128_load。VM 必須猜測類型。 |
_mm_loadh_pd |
❌ 沒有 Wasm SIMD 支援。 |
_mm_loadl_epi64 |
❌ 沒有 Wasm SIMD 支援。 |
_mm_loadl_pd |
❌ 沒有 Wasm SIMD 支援。 |
_mm_loadr_pd |
💡 虛擬。Simd 載入 + 混洗。 |
_mm_loadu_pd |
🟡 wasm_v128_load。VM 必須猜測類型。 |
_mm_loadu_si128 |
🟡 wasm_v128_load。VM 必須猜測類型。 |
_mm_loadu_si64 |
❌ 使用 const+純量載入+取代通道模擬 |
_mm_loadu_si32 |
❌ 使用 const+純量載入+取代通道模擬 |
_mm_loadu_si16 |
❌ 使用 const+純量載入+取代通道模擬 |
_mm_madd_epi16 |
✅ wasm_i32x4_dot_i16x8 |
_mm_maskmoveu_si128 |
❌ 純量化 |
_mm_max_epi16 |
✅ wasm_i16x8_max |
_mm_max_epu8 |
✅ wasm_u8x16_max |
_mm_max_pd |
TODO: 遷移到 wasm_f64x2_pmax |
_mm_max_sd |
⚠️ 使用混洗模擬 |
_mm_mfence |
⚠️ 在多執行緒建置中,會完全阻礙。 |
_mm_min_epi16 |
✅ wasm_i16x8_min |
_mm_min_epu8 |
✅ wasm_u8x16_min |
_mm_min_pd |
TODO: 遷移到 wasm_f64x2_pmin |
_mm_min_sd |
⚠️ 使用混洗模擬 |
_mm_move_epi64 |
💡 使用混洗模擬。VM 必須猜測類型。 |
_mm_move_sd |
💡 使用混洗模擬。VM 必須猜測類型。 |
_mm_movemask_epi8 |
✅ wasm_i8x16_bitmask |
_mm_movemask_pd |
✅ wasm_i64x2_bitmask |
_mm_mul_epu32 |
⚠️ 使用 wasm_u64x2_extmul_low_u32x4 + 2 次洗牌模擬 |
_mm_mul_pd |
✅ wasm_f64x2_mul |
_mm_mul_sd |
⚠️ 使用混洗模擬 |
_mm_mulhi_epi16 |
⚠️ 使用 2 倍 SIMD extmul+通用洗牌模擬 |
_mm_mulhi_epu16 |
⚠️ 使用 2 倍 SIMD extmul+通用洗牌模擬 |
_mm_mullo_epi16 |
✅ wasm_i16x8_mul |
_mm_or_pd |
🟡 wasm_v128_or。VM 必須猜測類型。 |
_mm_or_si128 |
🟡 wasm_v128_or。VM 必須猜測類型。 |
_mm_packs_epi16 |
✅ wasm_i8x16_narrow_i16x8 |
_mm_packs_epi32 |
✅ wasm_i16x8_narrow_i32x4 |
_mm_packus_epi16 |
✅ wasm_u8x16_narrow_i16x8 |
_mm_pause |
💭 無操作。 |
_mm_sad_epu8 |
⚠️ 使用 11 個 SIMD 指令 + const 模擬 |
_mm_set_epi16 |
✅ wasm_i16x8_make |
_mm_set_epi32 |
✅ wasm_i32x4_make |
_mm_set_epi64 (_mm_set_epi64x) |
✅ wasm_i64x2_make |
_mm_set_epi8 |
✅ wasm_i8x16_make |
_mm_set_pd |
✅ wasm_f64x2_make |
_mm_set_sd |
💡 使用 wasm_f64x2_make 模擬 |
_mm_set1_epi16 |
✅ wasm_i16x8_splat |
_mm_set1_epi32 |
✅ wasm_i32x4_splat |
_mm_set1_epi64 (_mm_set1_epi64x) |
✅ wasm_i64x2_splat |
_mm_set1_epi8 |
✅ wasm_i8x16_splat |
_mm_set1_pd (_mm_set_pd1) |
✅ wasm_f64x2_splat |
_mm_setr_epi16 |
✅ wasm_i16x8_make |
_mm_setr_epi32 |
✅ wasm_i32x4_make |
_mm_setr_epi64 |
✅ wasm_i64x2_make |
_mm_setr_epi8 |
✅ wasm_i8x16_make |
_mm_setr_pd |
✅ wasm_f64x2_make |
_mm_setzero_pd |
💡 使用 wasm_f64x2_const 模擬 |
_mm_setzero_si128 |
💡 使用 wasm_i64x2_const 模擬 |
_mm_shuffle_epi32 |
💡 使用通用洗牌模擬 |
_mm_shuffle_pd |
💡 使用通用洗牌模擬 |
_mm_shufflehi_epi16 |
💡 使用通用洗牌模擬 |
_mm_shufflelo_epi16 |
💡 使用通用洗牌模擬 |
_mm_sll_epi16 |
❌ 純量化 |
_mm_sll_epi32 |
❌ 純量化 |
_mm_sll_epi64 |
❌ 純量化 |
_mm_slli_epi16 |
💡 wasm_i16x8_shl |
_mm_slli_epi32 |
💡 wasm_i32x4_shl |
_mm_slli_epi64 |
💡 wasm_i64x2_shl |
_mm_slli_si128 (_mm_bslli_si128) |
💡 使用通用洗牌模擬 |
_mm_sqrt_pd |
✅ wasm_f64x2_sqrt |
_mm_sqrt_sd |
⚠️ 使用混洗模擬 |
_mm_sra_epi16 |
❌ 純量化 |
_mm_sra_epi32 |
❌ 純量化 |
_mm_srai_epi16 |
💡 wasm_i16x8_shr |
_mm_srai_epi32 |
💡 wasm_i32x4_shr |
_mm_srl_epi16 |
❌ 純量化 |
_mm_srl_epi32 |
❌ 純量化 |
_mm_srl_epi64 |
❌ 純量化 |
_mm_srli_epi16 |
💡 wasm_u16x8_shr |
_mm_srli_epi32 |
💡 wasm_u32x4_shr |
_mm_srli_epi64 |
💡 wasm_u64x2_shr |
_mm_srli_si128 (_mm_bsrli_si128) |
💡 使用通用洗牌模擬 |
_mm_store_pd |
🟡 wasm_v128_store。VM 必須猜測類型。 |
_mm_store_sd |
💡 使用純量儲存模擬 |
_mm_store_si128 |
🟡 wasm_v128_store。VM 必須猜測類型。 |
_mm_store1_pd (_mm_store_pd1) |
🟡 虛擬。使用混洗模擬。 |
_mm_storeh_pd |
❌ 混洗 + 純量儲存 |
_mm_storel_epi64 |
❌ 純量儲存 |
_mm_storel_pd |
❌ 純量儲存 |
_mm_storer_pd |
❌ 混洗 + 純量儲存 |
_mm_storeu_pd |
🟡 wasm_v128_store。VM 必須猜測類型。 |
_mm_storeu_si128 |
🟡 wasm_v128_store。VM 必須猜測類型。 |
_mm_storeu_si64 |
💡 使用提取通道+純量儲存模擬 |
_mm_storeu_si32 |
💡 使用提取通道+純量儲存模擬 |
_mm_storeu_si16 |
💡 使用提取通道+純量儲存模擬 |
_mm_stream_pd |
🟡 wasm_v128_store。VM 必須猜測類型。 |
_mm_stream_si128 |
🟡 wasm_v128_store。VM 必須猜測類型。 |
_mm_stream_si32 |
🟡 wasm_v128_store。VM 必須猜測類型。 |
_mm_stream_si64 |
🟡 wasm_v128_store。VM 必須猜測類型。 |
_mm_sub_epi16 |
✅ wasm_i16x8_sub |
_mm_sub_epi32 |
✅ wasm_i32x4_sub |
_mm_sub_epi64 |
✅ wasm_i64x2_sub |
_mm_sub_epi8 |
✅ wasm_i8x16_sub |
_mm_sub_pd |
✅ wasm_f64x2_sub |
_mm_sub_sd |
⚠️ 使用混洗模擬 |
_mm_subs_epi16 |
✅ wasm_i16x8_sub_sat |
_mm_subs_epi8 |
✅ wasm_i8x16_sub_sat |
_mm_subs_epu16 |
✅ wasm_u16x8_sub_sat |
_mm_subs_epu8 |
✅ wasm_u8x16_sub_sat |
_mm_ucomieq_sd |
❌ 純量化 |
_mm_ucomige_sd |
❌ 純量化 |
_mm_ucomigt_sd |
❌ 純量化 |
_mm_ucomile_sd |
❌ 純量化 |
_mm_ucomilt_sd |
❌ 純量化 |
_mm_ucomineq_sd |
❌ 純量化 |
_mm_undefined_pd |
✅ 虛擬 |
_mm_undefined_si128 |
✅ 虛擬 |
_mm_unpackhi_epi16 |
💡 使用混洗模擬 |
_mm_unpackhi_epi32 |
💡 使用混洗模擬 |
_mm_unpackhi_epi64 |
💡 使用混洗模擬 |
_mm_unpackhi_epi8 |
💡 使用混洗模擬 |
_mm_unpachi_pd |
💡 使用混洗模擬 |
_mm_unpacklo_epi16 |
💡 使用混洗模擬 |
_mm_unpacklo_epi32 |
💡 使用混洗模擬 |
_mm_unpacklo_epi64 |
💡 使用混洗模擬 |
_mm_unpacklo_epi8 |
💡 使用混洗模擬 |
_mm_unpacklo_pd |
💡 使用混洗模擬 |
_mm_xor_pd |
🟡 wasm_v128_xor。VM 必須猜測類型。 |
_mm_xor_si128 |
🟡 wasm_v128_xor。VM 必須猜測類型。 |
_mm_add_si64, _mm_movepi64_pi64, _mm_movpi64_epi64, _mm_mul_su32, _mm_sub_si64, _mm_cvtpd_pi32, _mm_cvtpi32_pd, _mm_cvttpd_pi32
任何引用這些內建函數的程式碼都無法編譯。
下表重點說明了不同 SSE3 內建函數的可用性和預期效能。請參閱Intel 內建函數指南中的 SSE3。
內建函數名稱 |
WebAssembly SIMD 支援 |
---|---|
_mm_lddqu_si128 |
✅ wasm_v128_load。 |
_mm_addsub_ps |
⚠️ 使用 SIMD 加法 + 乘法 + 常數模擬 |
_mm_hadd_ps |
⚠️ 使用 SIMD 加法 + 兩次洗牌模擬 |
_mm_hsub_ps |
⚠️ 使用 SIMD 減法 + 兩次洗牌模擬 |
_mm_movehdup_ps |
💡 使用通用洗牌模擬 |
_mm_moveldup_ps |
💡 使用通用洗牌模擬 |
_mm_addsub_pd |
⚠️ 使用 SIMD 加法 + 乘法 + 常數模擬 |
_mm_hadd_pd |
⚠️ 使用 SIMD 加法 + 兩次洗牌模擬 |
_mm_hsub_pd |
⚠️ 使用 SIMD 加法 + 兩次洗牌模擬 |
_mm_loaddup_pd |
🟡 虛擬化。wasm_v64x2_load_splat,虛擬機器必須猜測類型。 |
_mm_movedup_pd |
💡 使用通用洗牌模擬 |
_MM_GET_DENORMALS_ZERO_MODE |
✅ 總是回傳 _MM_DENORMALS_ZERO_ON。也就是說,次正規值可用。 |
_MM_SET_DENORMALS_ZERO_MODE |
⚫ 不可用。固定為 _MM_DENORMALS_ZERO_ON。 |
_mm_monitor |
⚫ 不可用。 |
_mm_mwait |
⚫ 不可用。 |
下表重點說明了不同 SSSE3 內建函數的可用性和預期效能。請參閱Intel 內建函數指南中的 SSSE3。
內建函數名稱 |
WebAssembly SIMD 支援 |
---|---|
_mm_abs_epi8 |
✅ wasm_i8x16_abs |
_mm_abs_epi16 |
✅ wasm_i16x8_abs |
_mm_abs_epi32 |
✅ wasm_i32x4_abs |
_mm_alignr_epi8 |
⚠️ 使用 SIMD 或 + 兩次位移模擬 |
_mm_hadd_epi16 |
⚠️ 使用 SIMD 加法 + 兩次洗牌模擬 |
_mm_hadd_epi32 |
⚠️ 使用 SIMD 加法 + 兩次洗牌模擬 |
_mm_hadds_epi16 |
⚠️ 使用 SIMD 加飽和 + 兩次洗牌模擬 |
_mm_hsub_epi16 |
⚠️ 使用 SIMD 減法 + 兩次洗牌模擬 |
_mm_hsub_epi32 |
⚠️ 使用 SIMD 減法 + 兩次洗牌模擬 |
_mm_hsubs_epi16 |
⚠️ 使用 SIMD 減飽和 + 兩次洗牌模擬 |
_mm_maddubs_epi16 |
⚠️ 使用 SIMD 飽和加法 + 四次位移 + 兩次乘法 + and + const 模擬 |
_mm_mulhrs_epi16 |
⚠️ 使用 SIMD 四次擴展 + 兩次乘法 + 四次加法 + 複雜洗牌 + const 模擬 |
_mm_shuffle_epi8 |
⚠️ 使用 SIMD 旋轉 + and + const 模擬 |
_mm_sign_epi8 |
⚠️ 使用 SIMD 兩次比較 + 兩次邏輯 + 加法模擬 |
_mm_sign_epi16 |
⚠️ 使用 SIMD 兩次比較 + 兩次邏輯 + 加法模擬 |
_mm_sign_epi32 |
⚠️ 使用 SIMD 兩次比較 + 兩次邏輯 + 加法模擬 |
_mm_abs_pi8, _mm_abs_pi16, _mm_abs_pi32, _mm_alignr_pi8, _mm_hadd
任何引用這些內建函數的程式碼都無法編譯。
下表重點說明了不同 SSE4.1 內建函數的可用性和預期效能。請參閱Intel SSE4.1 內建函數指南。
內建函數名稱 |
WebAssembly SIMD 支援 |
---|---|
_mm_blend_epi16 |
💡 使用通用洗牌模擬 |
_mm_blend_pd |
💡 使用通用洗牌模擬 |
_mm_blend_ps |
💡 使用通用洗牌模擬 |
_mm_blendv_epi8 |
⚠️ 使用 SIMD shr+and+andnot+or 模擬 |
_mm_blendv_pd |
⚠️ 使用 SIMD shr+and+andnot+or 模擬 |
_mm_blendv_ps |
⚠️ 使用 SIMD shr+and+andnot+or 模擬 |
_mm_ceil_pd |
✅ wasm_f64x2_ceil |
_mm_ceil_ps |
✅ wasm_f32x4_ceil |
_mm_ceil_sd |
⚠️ 使用混洗模擬 |
_mm_ceil_ss |
⚠️ 使用混洗模擬 |
_mm_cmpeq_epi64 |
⚠️ 使用 SIMD cmp+and+shuffle 模擬 |
_mm_cvtepi16_epi32 |
✅ wasm_i32x4_widen_low_i16x8 |
_mm_cvtepi16_epi64 |
⚠️ 使用 SIMD widen+const+cmp+shuffle 模擬 |
_mm_cvtepi32_epi64 |
⚠️ 使用 SIMD const+cmp+shuffle 模擬 |
_mm_cvtepi8_epi16 |
✅ wasm_i16x8_widen_low_i8x16 |
_mm_cvtepi8_epi32 |
⚠️ 使用兩個 SIMD widen 模擬 |
_mm_cvtepi8_epi64 |
⚠️ 使用兩個 SIMD widen+const+cmp+shuffle 模擬 |
_mm_cvtepu16_epi32 |
✅ wasm_u32x4_extend_low_u16x8 |
_mm_cvtepu16_epi64 |
⚠️ 使用 SIMD const+兩個 shuffle 模擬 |
_mm_cvtepu32_epi64 |
⚠️ 使用 SIMD const+shuffle 模擬 |
_mm_cvtepu8_epi16 |
✅ wasm_u16x8_extend_low_u8x16 |
_mm_cvtepu8_epi32 |
⚠️ 使用兩個 SIMD widen 模擬 |
_mm_cvtepu8_epi64 |
⚠️ 使用 SIMD const+三個 shuffle 模擬 |
_mm_dp_pd |
⚠️ 使用 SIMD mul+add+setzero+2xblend 模擬 |
_mm_dp_ps |
⚠️ 使用 SIMD mul+add+setzero+2xblend 模擬 |
_mm_extract_epi32 |
✅ wasm_i32x4_extract_lane |
_mm_extract_epi64 |
✅ wasm_i64x2_extract_lane |
_mm_extract_epi8 |
✅ wasm_u8x16_extract_lane |
_mm_extract_ps |
✅ wasm_i32x4_extract_lane |
_mm_floor_pd |
✅ wasm_f64x2_floor |
_mm_floor_ps |
✅ wasm_f32x4_floor |
_mm_floor_sd |
⚠️ 使用混洗模擬 |
_mm_floor_ss |
⚠️ 使用混洗模擬 |
_mm_insert_epi32 |
✅ wasm_i32x4_replace_lane |
_mm_insert_epi64 |
✅ wasm_i64x2_replace_lane |
_mm_insert_epi8 |
✅ wasm_i8x16_replace_lane |
_mm_insert_ps |
⚠️ 使用通用的非 SIMD 對應 shuffle 模擬 |
_mm_max_epi32 |
✅ wasm_i32x4_max |
_mm_max_epi8 |
✅ wasm_i8x16_max |
_mm_max_epu16 |
✅ wasm_u16x8_max |
_mm_max_epu32 |
✅ wasm_u32x4_max |
_mm_min_epi32 |
✅ wasm_i32x4_min |
_mm_min_epi8 |
✅ wasm_i8x16_min |
_mm_min_epu16 |
✅ wasm_u16x8_min |
_mm_min_epu32 |
✅ wasm_u32x4_min |
_mm_minpos_epu16 |
💣 純量化 |
_mm_mpsadbw_epu8 |
💣 純量化 |
_mm_mul_epi32 |
⚠️ 使用 wasm_i64x2_extmul_low_i32x4 + 2 個 shuffle 模擬 |
_mm_mullo_epi32 |
✅ wasm_i32x4_mul |
_mm_packus_epi32 |
✅ wasm_u16x8_narrow_i32x4 |
_mm_round_pd |
✅ wasm_f64x2_ceil/wasm_f64x2_floor/wasm_f64x2_nearest/wasm_f64x2_trunc |
_mm_round_ps |
✅ wasm_f32x4_ceil/wasm_f32x4_floor/wasm_f32x4_nearest/wasm_f32x4_trunc |
_mm_round_sd |
⚠️ 使用混洗模擬 |
_mm_round_ss |
⚠️ 使用混洗模擬 |
_mm_stream_load_si128 |
🟡 wasm_v128_load。VM 必須猜測類型。 |
_mm_test_all_ones |
❌ 純量化 |
_mm_test_all_zeros |
❌ 純量化 |
_mm_test_mix_ones_zeros |
❌ 純量化 |
_mm_testc_si128 |
❌ 純量化 |
_mm_testnzc_si128 |
❌ 純量化 |
_mm_testz_si128 |
❌ 純量化 |
下表重點說明了不同 SSE4.2 內建函數的可用性和預期效能。請參閱Intel SSE4.2 內建函數指南。
內建函數名稱 |
WebAssembly SIMD 支援 |
---|---|
_mm_cmpgt_epi64 |
✅ wasm_i64x2_gt |
_mm_cmpestra、_mm_cmpestrc、_mm_cmpestri、_mm_cmpestrm、_mm_cmpestro、_mm_cmpestrs、_mm_cmpestrz、_mm_cmpistra、_mm_cmpistrc、_mm_cmpistri、_mm_cmpistrm、_mm_cmpistro、_mm_cmpistrs、_mm_cmpistrz、_mm_crc32_u16、_mm_crc32_u32、_mm_crc32_u64、_mm_crc32_u8
任何引用這些內建函數的程式碼都無法編譯。
下表重點說明了不同 AVX 內建函數的可用性和預期效能。請參閱Intel AVX 內建函數指南。
內建函數名稱 |
WebAssembly SIMD 支援 |
---|---|
_mm_broadcast_ss |
✅ wasm_v32x4_load_splat |
_mm_cmp_pd |
⚠️ 使用 1-2 個 SIMD cmp+and/or 模擬 |
_mm_cmp_ps |
⚠️ 使用 1-2 個 SIMD cmp+and/or 模擬 |
_mm_cmp_sd |
⚠️ 使用 1-2 個 SIMD cmp+and/or+move 模擬 |
_mm_cmp_ss |
⚠️ 使用 1-2 個 SIMD cmp+and/or+move 模擬 |
_mm_maskload_pd |
⚠️ 使用 SIMD load+shift+and 模擬 |
_mm_maskload_ps |
⚠️ 使用 SIMD load+shift+and 模擬 |
_mm_maskstore_pd |
❌ 純量化 |
_mm_maskstore_ps |
❌ 純量化 |
_mm_permute_pd |
💡 使用通用洗牌模擬 |
_mm_permute_ps |
💡 使用通用洗牌模擬 |
_mm_permutevar_pd |
💣 純量化 |
_mm_permutevar_ps |
💣 純量化 |
_mm_testc_pd |
💣 使用複雜的 SIMD+純量序列模擬 |
_mm_testc_ps |
💣 使用複雜的 SIMD+純量序列模擬 |
_mm_testnzc_pd |
💣 使用複雜的 SIMD+純量序列模擬 |
_mm_testnzc_ps |
💣 使用複雜的 SIMD+純量序列模擬 |
_mm_testz_pd |
💣 使用複雜的 SIMD+純量序列模擬 |
_mm_testz_ps |
💣 使用複雜的 SIMD+純量序列模擬 |
僅列出 AVX 指令集中 128 位元寬的指令。256 位元寬的 AVX 指令由兩個 128 位元寬的指令模擬。
Emscripten 支援透過將 -mfpu=neon 指令傳遞給編譯器,並包含標頭 <arm_neon.h>,來編譯使用 ARM NEON 的現有程式碼庫。
就效能而言,非常重要的是要注意,僅乾淨地支援對 128 位元寬向量進行操作的指令。這表示幾乎任何非 “q” 變體的指令 (即 “vaddq” 而不是 “vadd”) 都會被純量化。
這些是從 GitHub 上的 SIMDe 儲存庫中提取的。若要使用最新的 SIMDe 版本更新 emscripten,請執行 tools/simde_update.py。
下表重點說明了各種 128 位元寬內建函數的可用性。
✅ Wasm SIMD 具有與 NEON 指令相符的原生操作碼,應產生原生效能
💡 雖然 Wasm SIMD 規格未提供適當的效能保證,但如果編譯器和執行階段 VM 路徑夠聰明,此內建函數應該能夠產生相同的原生 NEON 指令。
⚠️ 底層 NEON 指令不可用,但它最多透過其他幾個 Wasm SIMD 指令模擬,導致小的效能損失。
❌ Wasm SIMD 規格未公開底層 NEON 指令,因此必須透過較慢的路徑模擬,例如,一系列較慢的 SIMD 指令或純量實作。
⚫ 給定的 NEON 內建函數不可用。引用內建函數將導致編譯器錯誤。
有關每個內建函數的詳細資訊,請參閱NEON 內建函數參考。
如需最新的 NEON 內建函數實作狀態,請參閱SIMDe 實作狀態。
內建函數名稱 |
Wasm SIMD 支援 |
---|---|
vaba |
❌ 將透過慢速指令模擬或純量化 |
vabaq |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vabal |
⚫ 未實作,將觸發編譯器錯誤 |
vabd |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vabdq |
✅ 原生 |
vabdl |
❌ 將透過慢速指令模擬或純量化 |
vabs |
❌ 將透過慢速指令模擬或純量化 |
vabq |
✅ 原生 |
vadd |
❌ 將透過慢速指令模擬或純量化 |
vaddq_s & vaddq_f |
✅ 原生 |
vaddhn |
💡 取決於編譯器是否夠聰明,但應接近原生 |
vaddl |
❌ 將透過慢速指令模擬或純量化 |
vaddlv |
❌ 將透過慢速指令模擬或純量化 |
vaddv |
❌ 將透過慢速指令模擬或純量化 |
vaddw |
❌ 將透過慢速指令模擬或純量化 |
vand |
✅ 原生 |
vbcaxq |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vbic |
❌ 將透過慢速指令模擬或純量化 |
vbiq |
✅ 原生 |
vbsl |
✅ 原生 |
vcage |
❌ 將透過慢速指令模擬或純量化 |
vcagt |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vceq |
💡 取決於編譯器是否夠聰明,但應接近原生 |
vceqz |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vcge |
✅ 原生 |
vcgez |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vcgt |
✅ 原生 |
vcgtz |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vcle |
✅ 原生 |
vclez |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vcls |
❌ 將透過慢速指令模擬或純量化 |
vclt |
✅ 原生 |
vcltz |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vcmla、vcmla_rot90、cmla_rot180、cmla_rot270 |
❌ 將透過慢速指令模擬或純量化 |
vcmlq |
✅ 原生 |
vcnt |
❌ 將透過慢速指令模擬或純量化 |
vclz |
❌ 將透過慢速指令模擬或純量化 |
vcombine |
❌ 將透過慢速指令模擬或純量化 |
vcreate |
❌ 將透過慢速指令模擬或純量化 |
vdot |
❌ 將透過慢速指令模擬或純量化 |
vdot_lane |
❌ 將透過慢速指令模擬或純量化 |
vdup |
❌ 將透過慢速指令模擬或純量化 |
vdup_n |
✅ 原生 |
veor |
✅ 原生 |
vext |
❌ 將透過慢速指令模擬或純量化 |
vfma、vfma_lane、vfma_n |
❌ 將透過慢速指令模擬或純量化 |
vget_lane |
✅ 原生 |
vhadd |
❌ 將透過慢速指令模擬或純量化 |
vhsub |
❌ 將透過慢速指令模擬或純量化 |
vld1 |
✅ 原生 |
vld2 |
❌ 將透過慢速指令模擬或純量化 |
vld3 |
💡 取決於編譯器是否夠聰明,但應接近原生 |
vld4 |
💡 取決於編譯器是否夠聰明,但應接近原生 |
vld4_lane |
❌ 將透過慢速指令模擬或純量化 |
vmax |
✅ 原生 |
vmaxv |
❌ 將透過慢速指令模擬或純量化 |
vmin |
✅ 原生 |
vminv |
❌ 將透過慢速指令模擬或純量化 |
vmla |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vmlal |
❌ 將透過慢速指令模擬或純量化 |
vmlal_high_n |
❌ 將透過慢速指令模擬或純量化 |
vmlal_lane |
❌ 將透過慢速指令模擬或純量化 |
vmls |
❌ 將透過慢速指令模擬或純量化 |
vmls_n |
❌ 將透過慢速指令模擬或純量化 |
vmlsl |
❌ 將透過慢速指令模擬或純量化 |
vmlsl_high |
❌ 將透過慢速指令模擬或純量化 |
vmlsl_high_n |
❌ 將透過慢速指令模擬或純量化 |
vmlsl_lane |
❌ 將透過慢速指令模擬或純量化 |
vmovl |
✅ 原生 |
vmul |
✅ 原生 |
vmul_n |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vmull |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vmull_n |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vmull_lane |
❌ 將透過慢速指令模擬或純量化 |
vmull_high |
❌ 將透過慢速指令模擬或純量化 |
vmvn |
✅ 原生 |
vneg |
✅ 原生 |
vorn |
❌ 將透過慢速指令模擬或純量化 |
vorr |
✅ 原生 |
vpadal |
❌ 將透過慢速指令模擬或純量化 |
vpadd |
❌ 將透過慢速指令模擬或純量化 |
vpaddl |
❌ 將透過慢速指令模擬或純量化 |
vpmax |
❌ 將透過慢速指令模擬或純量化 |
vpmin |
❌ 將透過慢速指令模擬或純量化 |
vpminnm |
⚫ 未實作,將觸發編譯器錯誤 |
vqabs |
❌ 將透過慢速指令模擬或純量化 |
vqabsb |
❌ 將透過慢速指令模擬或純量化 |
vqadd |
💡 取決於編譯器是否夠聰明,但應接近原生 |
vqaddb |
❌ 將透過慢速指令模擬或純量化 |
vqdmulh |
❌ 將透過慢速指令模擬或純量化 |
vqdmulh_lane |
❌ 將透過慢速指令模擬或純量化 |
vqneg |
❌ 將透過慢速指令模擬或純量化 |
vqnegb |
❌ 將透過慢速指令模擬或純量化 |
vqrdmulh |
❌ 將透過慢速指令模擬或純量化 |
vqrdmulh_lane |
❌ 將透過慢速指令模擬或純量化 |
vqshl |
❌ 將透過慢速指令模擬或純量化 |
vqshlb |
❌ 將透過慢速指令模擬或純量化 |
vqshrn_n |
❌ 將透過慢速指令模擬或純量化 |
vqshrun_n |
❌ 將透過慢速指令模擬或純量化 |
vqsub |
❌ 將透過慢速指令模擬或純量化 |
vqsubb |
❌ 將透過慢速指令模擬或純量化 |
vqtbl1 |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vqtbl2 |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vqtbl3 |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vqtbl4 |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vqtbx1 |
❌ 將透過慢速指令模擬或純量化 |
vqtbx2 |
❌ 將透過慢速指令模擬或純量化 |
vqtbx3 |
❌ 將透過慢速指令模擬或純量化 |
vqtbx4 |
❌ 將透過慢速指令模擬或純量化 |
vrbit |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vrecpe |
❌ 將透過慢速指令模擬或純量化 |
vrecps |
❌ 將透過慢速指令模擬或純量化 |
vreinterpret |
💡 取決於編譯器是否夠聰明,但應接近原生 |
vrev16 |
✅ 原生 |
vrev32 |
✅ 原生 |
vrev64 |
✅ 原生 |
vrhadd |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vrsh_n |
❌ 將透過慢速指令模擬或純量化 |
vrshn_n |
❌ 將透過慢速指令模擬或純量化 |
vrsqrte |
❌ 將透過慢速指令模擬或純量化 |
vrsqrts |
❌ 將透過慢速指令模擬或純量化 |
vrshl |
❌ 將透過慢速指令模擬或純量化 |
vrshr_n |
❌ 將透過慢速指令模擬或純量化 |
vrsra_n |
❌ 將透過慢速指令模擬或純量化 |
vset_lane |
✅ 原生 |
vshl |
純量化 |
vshl_n |
❌ 將透過慢速指令模擬或純量化 |
vshll_n |
❌ 將透過慢速指令模擬或純量化 |
vshr_n |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vshrn_n |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vsqadd |
❌ 將透過慢速指令模擬或純量化 |
vsra_n |
❌ 將透過慢速指令模擬或純量化 |
vsri_n |
❌ 將透過慢速指令模擬或純量化 |
vst1 |
✅ 原生 |
vst1_lane |
💡 取決於編譯器是否夠聰明,但應接近原生 |
vst2 |
❌ 將透過慢速指令模擬或純量化 |
vst2_lane |
❌ 將透過慢速指令模擬或純量化 |
vst3 |
💡 取決於編譯器是否夠聰明,但應接近原生 |
vst3_lane |
❌ 將透過慢速指令模擬或純量化 |
vst4 |
💡 取決於編譯器是否夠聰明,但應接近原生 |
vst4_lane |
❌ 將透過慢速指令模擬或純量化 |
vsub |
✅ 原生 |
vsubl |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vsubl_high |
⚠️ 沒有直接實作,但使用快速 NEON 指令模擬 |
vsubn |
❌ 將透過慢速指令模擬或純量化 |
vsubw |
❌ 將透過慢速指令模擬或純量化 |
vtbl1 |
❌ 將透過慢速指令模擬或純量化 |
vtbl2 |
❌ 將透過慢速指令模擬或純量化 |
vtbl3 |
❌ 將透過慢速指令模擬或純量化 |
vtbl4 |
❌ 將透過慢速指令模擬或純量化 |
vtbx1 |
❌ 將透過慢速指令模擬或純量化 |
vtbx2 |
❌ 將透過慢速指令模擬或純量化 |
vtbx3 |
❌ 將透過慢速指令模擬或純量化 |
vtbx4 |
❌ 將透過慢速指令模擬或純量化 |
vtrn |
❌ 將透過慢速指令模擬或純量化 |
vtrn1 |
❌ 將透過慢速指令模擬或純量化 |
vtrn2 |
❌ 將透過慢速指令模擬或純量化 |
vtst |
❌ 將透過慢速指令模擬或純量化 |
vuqadd |
❌ 將透過慢速指令模擬或純量化 |
vuqaddb |
❌ 將透過慢速指令模擬或純量化 |
vuzp |
❌ 將透過慢速指令模擬或純量化 |
vuzp1 |
❌ 將透過慢速指令模擬或純量化 |
vuzp2 |
❌ 將透過慢速指令模擬或純量化 |
vxar |
❌ 將透過慢速指令模擬或純量化 |
vzip |
❌ 將透過慢速指令模擬或純量化 |
vzip1 |
❌ 將透過慢速指令模擬或純量化 |
vzip2 |
❌ 將透過慢速指令模擬或純量化 |