Emscripten unterstützt die WebAssembly SIMD Funktion. Es gibt fünf verschiedene Möglichkeiten, WebAssembly SIMD in Ihren C/C++-Programmen zu nutzen
Aktivieren Sie den LLVM/Clang SIMD Autovectorizer, um WebAssembly SIMD automatisch anzusprechen, ohne Änderungen am C/C++-Quellcode vornehmen zu müssen.
Schreiben Sie SIMD-Code unter Verwendung der GCC/Clang SIMD Vector Extensions (__attribute__((vector_size(16))))
Schreiben Sie SIMD-Code unter Verwendung der WebAssembly SIMD Intrinsics (#include <wasm_simd128.h>)
Kompilieren Sie vorhandenen SIMD-Code, der die x86 SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2 oder AVX Intrinsics verwendet (#include <*mmintrin.h>)
Kompilieren Sie vorhandenen SIMD-Code, der die ARM NEON Intrinsics verwendet (#include <arm_neon.h>)
Diese Techniken können in einem einzigen Programm frei kombiniert werden.
Um eine der fünf oben genannten SIMD-Typen zu aktivieren, übergeben Sie zur Kompilierzeit das WebAssembly-spezifische Flag -msimd128. Dadurch werden auch die Autovektorisierungs-Durchläufe von LLVM aktiviert. Falls dies nicht gewünscht ist, übergeben Sie zusätzlich die Flags -fno-vectorize -fno-slp-vectorize, um den Autovectorizer zu deaktivieren. Weitere Informationen finden Sie unter Auto-Vectorization in LLVM.
WebAssembly SIMD wird unterstützt von
Chrome ≥ 91 (Mai 2021),
Firefox ≥ 89 (Juni 2021),
Safari ≥ 16.4 (März 2023) und
Node.js ≥ 16.4 (Juni 2021).
Details zu anderen VMs finden Sie in der WebAssembly Roadmap.
Ein kommender Relaxed SIMD-Vorschlag wird weitere SIMD-Anweisungen zu WebAssembly hinzufügen.
Auf Quellcodeebene können die GCC/Clang SIMD Vector Extensions verwendet werden und werden, wo immer möglich, in WebAssembly SIMD-Instruktionen übersetzt.
Dies ermöglicht Entwicklern, benutzerdefinierte breite Vektortypen über Typedefs zu erstellen und arithmetische Operatoren (+,-,*,/) auf vektorisierten Typen zu verwenden, sowie den individuellen Spaltenzugriff über die Vektor[i]-Notation zuzulassen. Die GCC Vektor-Built-in-Funktionen sind jedoch nicht verfügbar. Verwenden Sie stattdessen die untenstehenden WebAssembly SIMD Intrinsics-Funktionen.
LLVM pflegt eine WebAssembly SIMD Intrinsics Header-Datei, die mit Emscripten bereitgestellt wird und Typdefinitionen für die verschiedenen unterstützten Vektortypen hinzufügt.
#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 }
Die Wasm SIMD Header-Datei kann online unter wasm_simd128.h eingesehen werden.
Übergeben Sie zur Kompilierzeit das Flag -msimd128, um die Verwendung von WebAssembly SIMD Intrinsics zu ermöglichen. C/C++-Code kann die integrierte Präprozessor-Definition #ifdef __wasm_simd128__ verwenden, um zu erkennen, wann mit aktiviertem WebAssembly SIMD kompiliert wird.
Übergeben Sie -mrelaxed-simd, um WebAssembly Relaxed SIMD Intrinsics anzusprechen. C/C++-Code kann die integrierte Präprozessor-Definition #ifdef __wasm_relaxed_simd__ verwenden, um zu erkennen, wann dieses Ziel aktiv ist.
Beim Portieren von nativem SIMD-Code sollte beachtet werden, dass die WebAssembly SIMD-Spezifikation aus Gründen der Portabilität nicht alle nativen x86/ARM SIMD-Anweisungen offenlegt. Insbesondere bestehen folgende Änderungen
Emscripten unterstützt keine x86- oder andere native Inline-SIMD-Assembly oder das Erstellen von .s-Assembly-Dateien, daher sollte der gesamte Code so geschrieben werden, dass er SIMD-Intrinsic-Funktionen oder Compiler-Vektor-Erweiterungen verwendet.
WebAssembly SIMD hat keine Kontrolle über die Verwaltung von Gleitkomma-Rundungsmodi oder die Behandlung von Denormalen.
Cache-Line-Prefetch-Anweisungen sind nicht verfügbar, und Aufrufe dieser Funktionen werden kompiliert, aber als No-Ops behandelt.
Asymmetrische Speicherbarriere-Operationen sind nicht verfügbar, werden aber als vollständig synchrone Speicherbarrieren implementiert, wenn SharedArrayBuffer aktiviert ist (-pthread), oder als No-Ops, wenn Multithreading nicht aktiviert ist (Standard).
SIMD-bezogene Fehlerberichte werden im Emscripten Bug-Tracker mit dem Label SIMD verfolgt.
Bei der Entwicklung von SIMD-Code zur Verwendung von WebAssembly SIMD sollten Implementierer die semantischen Unterschiede zwischen der Host-Hardware und den WebAssembly-Semantiken beachten; wie in der WebAssembly-Design-Dokumentation anerkannt, „führt dies manchmal zu schlechter Leistung.“ Die folgende Liste skizziert einige WebAssembly SIMD-Anweisungen, auf die bei der Leistungsoptimierung geachtet werden sollte
WebAssembly SIMD-Anweisung |
Arch |
Überlegungen |
|---|---|---|
[i8x16|i16x8|i32x4|i64x2].[shl|shr_s|shr_u] |
x86, Arm |
Verwenden Sie eine konstante Shift-Anzahl, um zusätzliche Überprüfungen der Grenzen zu vermeiden. |
i8x16.[shl|shr_s|shr_u] |
x86 |
Aus Orthogonalitätsgründen enthalten, haben diese Anweisungen keine äquivalente x86-Anweisung und werden mit 5-11 x86-Anweisungen in v8 emuliert (d.h. unter Verwendung von 16x8-Shifts). |
i64x2.shr_s |
x86 |
Aus Orthogonalitätsgründen enthalten, hat diese Anweisung keine äquivalente x86-Anweisung und wird mit 6-12 x86-Anweisungen in v8 emuliert. |
i8x16.swizzle |
x86 |
Das Null-Verhalten entspricht nicht x86 (d.h. diese Anweisung setzt auf Null, wenn ein Index außerhalb des Bereichs liegt, anstatt wenn das höchstwertige Bit 1 ist); verwenden Sie eine konstante Swizzle-Menge (oder i8x16.shuffle), um in einigen Laufzeiten 3 zusätzliche x86-Anweisungen zu vermeiden. |
[f32x4|f64x2].[min|max] |
x86 |
Wie bei den skalaren Versionen zwingt die NaN-Propagationssemantik Laufzeiten, mit 7-10 x86-Anweisungen zu emulieren (siehe z.B. v8s Emulation); verwenden Sie stattdessen, wenn möglich, [f32x4|f64x2].[pmin|pmax] (1 x86-Anweisung). |
i32x4.trunc_sat_f32x4_[u|s] |
x86 |
Keine äquivalente x86-Semantik; emuliert mit 8-14 x86-Anweisungen in v8. |
i32x4.trunc_sat_f64x2_[u|s]_zero |
x86 |
Keine äquivalente x86-Semantik; emuliert mit 5-6 x86-Anweisungen in v8. |
f32x4.convert_f32x4_u |
x86 |
Keine äquivalente x86-Semantik; emuliert mit 8 x86-Anweisungen in v8. |
[i8x16|i64x2].mul |
x86 |
Aus Orthogonalitätsgründen enthalten, haben diese Anweisungen keine äquivalente x86-Anweisung und werden mit 10 x86-Anweisungen in v8 emuliert. |
Emscripten unterstützt das Kompilieren bestehender Codebasen, die x86 SSE-Anweisungen verwenden, indem das Flag -msimd128 und zusätzlich eines der folgenden übergeben wird
SSE: übergeben Sie -msse und #include <xmmintrin.h>. Verwenden Sie #ifdef __SSE__, um Code zu schützen.
SSE2: übergeben Sie -msse2 und #include <emmintrin.h>. Verwenden Sie #ifdef __SSE2__, um Code zu schützen.
SSE3: übergeben Sie -msse3 und #include <pmmintrin.h>. Verwenden Sie #ifdef __SSE3__, um Code zu schützen.
SSSE3: übergeben Sie -mssse3 und #include <tmmintrin.h>. Verwenden Sie #ifdef __SSSE3__, um Code zu schützen.
SSE4.1: übergeben Sie -msse4.1 und #include <smmintrin.h>. Verwenden Sie #ifdef __SSE4_1__, um Code zu schützen.
SSE4.2: übergeben Sie -msse4.2 und #include <nmmintrin.h>. Verwenden Sie #ifdef __SSE4_2__, um Code zu schützen.
AVX: übergeben Sie -mavx und #include <immintrin.h>. Verwenden Sie #ifdef __AVX__, um Code zu schützen.
Derzeit werden nur die Befehlssätze SSE1, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2 und AVX unterstützt. Jeder dieser Befehlssätze baut auf den vorherigen auf, sodass z.B. bei der Verwendung von SSE3 auch die Befehlssätze SSE1 und SSE2 verfügbar sind.
Die folgenden Tabellen zeigen die Verfügbarkeit und erwartete Leistung verschiedener SSE*-Intrinsics. Dies kann nützlich sein, um die Leistungseinschränkungen der Wasm-SIMD-Spezifikation bei der Ausführung auf x86-Hardware zu verstehen.
Detaillierte Informationen zu jeder SSE-Intrinsic-Funktion finden Sie im hervorragenden Intel Intrinsics Guide zu SSE1.
✅ Wasm SIMD hat einen nativen Opcode, der der x86 SSE-Anweisung entspricht und native Leistung liefern sollte
💡 Obwohl die Wasm SIMD-Spezifikation keine ordnungsgemäße Leistungsgarantie bietet, sollte diese intrinsische Funktion bei einem ausreichend intelligenten Compiler und einem Laufzeit-VM-Pfad in der Lage sein, die identische native SSE-Anweisung zu generieren.
🟡 Es fehlen Informationen (z.B. Typ- oder Ausrichtungsinformationen), damit eine Wasm-VM garantiert den beabsichtigten x86-SSE-Opcode rekonstruieren kann. Dies könnte je nach Ziel-CPU-Hardwarefamilie, insbesondere bei älteren CPU-Generationen, zu einer Beeinträchtigung führen.
⚠️ Die zugrunde liegende x86 SSE-Anweisung ist nicht verfügbar, wird aber über höchstens wenige andere Wasm SIMD-Anweisungen emuliert, was eine geringe Leistungsbeeinträchtigung verursacht.
❌ Die zugrunde liegende x86 SSE-Anweisung wird von der Wasm SIMD-Spezifikation nicht offengelegt, daher muss sie über einen langsamen Pfad emuliert werden, z.B. eine Abfolge mehrerer langsamerer SIMD-Anweisungen oder eine skalare Implementierung.
💣 Der zugrunde liegende x86 SSE-Opcode ist in Wasm SIMD nicht verfügbar, und die Implementierung muss auf einen so langsamen emulierten Pfad zurückgreifen, dass ein Workaround, der den Algorithmus auf einer höheren Ebene überdenkt, empfohlen wird.
💭 Die angegebene SSE-Intrinsic ist verfügbar, damit Anwendungen kompiliert werden können, tut aber nichts.
⚫ Die angegebene SSE-Intrinsic ist nicht verfügbar. Das Referenzieren der Intrinsic führt zu einem Compilerfehler.
Bestimmte intrinsische Funktionen in der folgenden Tabelle sind als „virtuell“ gekennzeichnet. Dies bedeutet, dass es eigentlich keinen nativen x86 SSE-Befehlssatz-Opcode zu ihrer Implementierung gibt, aber native Compiler die Funktion als Komfortfunktion anbieten. Verschiedene Compiler können für diese unterschiedliche Befehlssequenzen generieren.
Zusätzlich zu den untenstehenden Tabellen können Sie Diagnosen für langsame, emulierte Funktionen aktivieren, indem Sie das Makro #define WASM_SIMD_COMPAT_SLOW definieren. Dies gibt Warnungen aus, wenn Sie versuchen, einen der langsamen Pfade (entsprechend ❌ oder 💣 in der Legende) zu verwenden.
Intrinsischer Name |
WebAssembly SIMD-Unterstützung |
|---|---|
_mm_set_ps |
✅ wasm_f32x4_make |
_mm_setr_ps |
✅ wasm_f32x4_make |
_mm_set_ss |
💡 emuliert mit wasm_f32x4_make |
_mm_set_ps1 (_mm_set1_ps) |
✅ wasm_f32x4_splat |
_mm_setzero_ps |
💡 emuliert mit wasm_f32x4_const(0) |
_mm_load_ps |
🟡 wasm_v128_load. VM muss Typ erraten. |
_mm_loadl_pi |
❌ Keine Wasm SIMD Unterstützung. |
_mm_loadh_pi |
❌ Keine Wasm SIMD Unterstützung. |
_mm_loadr_ps |
💡 Virtuell. Simd-Laden + Mischen. |
_mm_loadu_ps |
🟡 wasm_v128_load. VM muss Typ erraten. |
_mm_load_ps1 (_mm_load1_ps) |
🟡 Virtuell. Simd-Laden + Mischen. |
_mm_load_ss |
❌ emuliert mit wasm_f32x4_make |
_mm_storel_pi |
❌ skalare Speicherungen |
_mm_storeh_pi |
❌ Mischen + skalare Speicherungen |
_mm_store_ps |
🟡 wasm_v128_store. VM muss Typ erraten. |
_mm_stream_ps |
🟡 wasm_v128_store. VM muss Typ erraten. |
_mm_prefetch |
💭 No-Op. |
_mm_sfence |
⚠️ Eine vollständige Barriere in Multithread-Builds. |
_mm_shuffle_ps |
🟡 wasm_i32x4_shuffle. VM muss Typ erraten. |
_mm_storer_ps |
💡 Virtuell. Mischen + Simd-Speichern. |
_mm_store_ps1 (_mm_store1_ps) |
🟡 Virtuell. Emuliert mit Mischen. |
_mm_store_ss |
💡 emuliert mit skalarem Speichern |
_mm_storeu_ps |
🟡 wasm_v128_store. VM muss Typ erraten. |
_mm_storeu_si16 |
💡 emuliert mit skalarem Speichern |
_mm_storeu_si64 |
💡 emuliert mit skalarem Speichern |
_mm_movemask_ps |
✅ wasm_i32x4_bitmask |
_mm_move_ss |
💡 emuliert mit einem Mischen. VM muss Typ erraten. |
_mm_add_ps |
✅ wasm_f32x4_add |
_mm_add_ss |
⚠️ emuliert mit einem Mischen |
_mm_sub_ps |
✅ wasm_f32x4_sub |
_mm_sub_ss |
⚠️ emuliert mit einem Mischen |
_mm_mul_ps |
✅ wasm_f32x4_mul |
_mm_mul_ss |
⚠️ emuliert mit einem Mischen |
_mm_div_ps |
✅ wasm_f32x4_div |
_mm_div_ss |
⚠️ emuliert mit einem Mischen |
_mm_min_ps |
TODO: pmin, sobald es funktioniert |
_mm_min_ss |
⚠️ emuliert mit einem Mischen |
_mm_max_ps |
TODO: pmax, sobald es funktioniert |
_mm_max_ss |
⚠️ emuliert mit einem Mischen |
_mm_rcp_ps |
❌ Keine Wasm SIMD Unterstützung. |
_mm_rcp_ss |
❌ Keine Wasm SIMD Unterstützung. |
_mm_sqrt_ps |
✅ wasm_f32x4_sqrt |
_mm_sqrt_ss |
⚠️ emuliert mit einem Mischen |
_mm_rsqrt_ps |
❌ Keine Wasm SIMD Unterstützung. |
_mm_rsqrt_ss |
❌ Keine Wasm SIMD Unterstützung. |
_mm_unpackhi_ps |
💡 emuliert mit einem Mischen |
_mm_unpacklo_ps |
💡 emuliert mit einem Mischen |
_mm_movehl_ps |
💡 emuliert mit einem Mischen |
_mm_movelh_ps |
💡 emuliert mit einem Mischen |
_MM_TRANSPOSE4_PS |
💡 emuliert mit einem Mischen |
_mm_cmplt_ps |
✅ wasm_f32x4_lt |
_mm_cmplt_ss |
⚠️ emuliert mit einem Mischen |
_mm_cmple_ps |
✅ wasm_f32x4_le |
_mm_cmple_ss |
⚠️ emuliert mit einem Mischen |
_mm_cmpeq_ps |
✅ wasm_f32x4_eq |
_mm_cmpeq_ss |
⚠️ emuliert mit einem Mischen |
_mm_cmpge_ps |
✅ wasm_f32x4_ge |
_mm_cmpge_ss |
⚠️ emuliert mit einem Mischen |
_mm_cmpgt_ps |
✅ wasm_f32x4_gt |
_mm_cmpgt_ss |
⚠️ emuliert mit einem Mischen |
_mm_cmpord_ps |
❌ emuliert mit 2xcmp+and |
_mm_cmpord_ss |
❌ emuliert mit 2xcmp+and+shuffle |
_mm_cmpunord_ps |
❌ emuliert mit 2xcmp+or |
_mm_cmpunord_ss |
❌ emuliert mit 2xcmp+or+shuffle |
_mm_and_ps |
🟡 wasm_v128_and. VM muss Typ erraten. |
_mm_andnot_ps |
🟡 wasm_v128_andnot. VM muss Typ erraten. |
_mm_or_ps |
🟡 wasm_v128_or. VM muss Typ erraten. |
_mm_xor_ps |
🟡 wasm_v128_xor. VM muss Typ erraten. |
_mm_cmpneq_ps |
✅ wasm_f32x4_ne |
_mm_cmpneq_ss |
⚠️ emuliert mit einem Mischen |
_mm_cmpnge_ps |
⚠️ emuliert mit not+ge |
_mm_cmpnge_ss |
⚠️ emuliert mit not+ge+shuffle |
_mm_cmpngt_ps |
⚠️ emuliert mit not+gt |
_mm_cmpngt_ss |
⚠️ emuliert mit not+gt+shuffle |
_mm_cmpnle_ps |
⚠️ emuliert mit not+le |
_mm_cmpnle_ss |
⚠️ emuliert mit not+le+shuffle |
_mm_cmpnlt_ps |
⚠️ emuliert mit not+lt |
_mm_cmpnlt_ss |
⚠️ emuliert mit not+lt+shuffle |
_mm_comieq_ss |
❌ skaliert |
_mm_comige_ss |
❌ skaliert |
_mm_comigt_ss |
❌ skaliert |
_mm_comile_ss |
❌ skaliert |
_mm_comilt_ss |
❌ skaliert |
_mm_comineq_ss |
❌ skaliert |
_mm_ucomieq_ss |
❌ skaliert |
_mm_ucomige_ss |
❌ skaliert |
_mm_ucomigt_ss |
❌ skaliert |
_mm_ucomile_ss |
❌ skaliert |
_mm_ucomilt_ss |
❌ skaliert |
_mm_ucomineq_ss |
❌ skaliert |
_mm_cvtsi32_ss (_mm_cvt_si2ss) |
❌ skaliert |
_mm_cvtss_si32 (_mm_cvt_ss2si) |
💣 skalar mit komplex emulierter Semantik |
_mm_cvttss_si32 (_mm_cvtt_ss2si) |
💣 skalar mit komplex emulierter Semantik |
_mm_cvtsi64_ss |
❌ skaliert |
_mm_cvtss_si64 |
💣 skalar mit komplex emulierter Semantik |
_mm_cvttss_si64 |
💣 skalar mit komplex emulierter Semantik |
_mm_cvtss_f32 |
💡 skalarer Abruf |
_mm_malloc |
✅ Reserviert Speicher mit angegebener Ausrichtung. |
_mm_free |
✅ Alias zu free(). |
_MM_GET_EXCEPTION_MASK |
✅ Gibt immer alle maskierten Ausnahmen (0x1f80) zurück. |
_MM_GET_EXCEPTION_STATE |
❌ Der Ausnahmezustand wird nicht verfolgt. Gibt immer 0 zurück. |
_MM_GET_FLUSH_ZERO_MODE |
✅ Gibt immer _MM_FLUSH_ZERO_OFF zurück. |
_MM_GET_ROUNDING_MODE |
✅ Gibt immer _MM_ROUND_NEAREST zurück. |
_mm_getcsr |
✅ Gibt immer _MM_FLUSH_ZERO_OFF zurück |
_MM_SET_EXCEPTION_MASK |
⚫ Nicht verfügbar. Festgelegt auf alle maskierten Ausnahmen. |
_MM_SET_EXCEPTION_STATE |
⚫ Nicht verfügbar. Festgelegt auf Null/klaren Zustand. |
_MM_SET_FLUSH_ZERO_MODE |
⚫ Nicht verfügbar. Festgelegt auf _MM_FLUSH_ZERO_OFF. |
_MM_SET_ROUNDING_MODE |
⚫ Nicht verfügbar. Festgelegt auf _MM_ROUND_NEAREST. |
_mm_setcsr |
⚫ Nicht verfügbar. |
_mm_undefined_ps |
✅ Virtuell |
_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 und _mm_stream_pi.
Jeder Code, der auf diese intrinsischen Funktionen verweist, wird nicht kompiliert.
Die folgende Tabelle hebt die Verfügbarkeit und erwartete Leistung verschiedener SSE2-Intrinsics hervor. Siehe Intel Intrinsics Guide zu SSE2.
Intrinsischer Name |
WebAssembly SIMD-Unterstützung |
|---|---|
_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 |
⚠️ emuliert mit einem Mischen |
_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 muss Typ erraten. |
_mm_and_si128 |
🟡 wasm_v128_and. VM muss Typ erraten. |
_mm_andnot_pd |
🟡 wasm_v128_andnot. VM muss Typ erraten. |
_mm_andnot_si128 |
🟡 wasm_v128_andnot. VM muss Typ erraten. |
_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 |
💭 No-op. Keine Cache-Hinweise in 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 |
⚠️ emuliert mit einem Mischen |
_mm_cmpge_pd |
✅ wasm_f64x2_ge |
_mm_cmpge_sd |
⚠️ emuliert mit einem Mischen |
_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 |
⚠️ emuliert mit einem Mischen |
_mm_cmple_pd |
✅ wasm_f64x2_le |
_mm_cmple_sd |
⚠️ emuliert mit einem Mischen |
_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 |
⚠️ emuliert mit einem Mischen |
_mm_cmpneq_pd |
✅ wasm_f64x2_ne |
_mm_cmpneq_sd |
⚠️ emuliert mit einem Mischen |
_mm_cmpnge_pd |
⚠️ emuliert mit not+ge |
_mm_cmpnge_sd |
⚠️ emuliert mit not+ge+shuffle |
_mm_cmpngt_pd |
⚠️ emuliert mit not+gt |
_mm_cmpngt_sd |
⚠️ emuliert mit not+gt+shuffle |
_mm_cmpnle_pd |
⚠️ emuliert mit not+le |
_mm_cmpnle_sd |
⚠️ emuliert mit not+le+shuffle |
_mm_cmpnlt_pd |
⚠️ emuliert mit not+lt |
_mm_cmpnlt_sd |
⚠️ emuliert mit not+lt+shuffle |
_mm_cmpord_pd |
❌ emuliert mit 2xcmp+and |
_mm_cmpord_sd |
❌ emuliert mit 2xcmp+and+shuffle |
_mm_cmpunord_pd |
❌ emuliert mit 2xcmp+or |
_mm_cmpunord_sd |
❌ emuliert mit 2xcmp+or+shuffle |
_mm_comieq_sd |
❌ skaliert |
_mm_comige_sd |
❌ skaliert |
_mm_comigt_sd |
❌ skaliert |
_mm_comile_sd |
❌ skaliert |
_mm_comilt_sd |
❌ skaliert |
_mm_comineq_sd |
❌ skaliert |
_mm_cvtepi32_pd |
✅ wasm_f64x2_convert_low_i32x4 |
_mm_cvtepi32_ps |
✅ wasm_f32x4_convert_i32x4 |
_mm_cvtpd_epi32 |
❌ skaliert |
_mm_cvtpd_ps |
✅ wasm_f32x4_demote_f64x2_zero |
_mm_cvtps_epi32 |
❌ skaliert |
_mm_cvtps_pd |
✅ wasm_f64x2_promote_low_f32x4 |
_mm_cvtsd_f64 |
✅ wasm_f64x2_extract_lane |
_mm_cvtsd_si32 |
❌ skaliert |
_mm_cvtsd_si64 |
❌ skaliert |
_mm_cvtsd_si64x |
❌ skaliert |
_mm_cvtsd_ss |
❌ skaliert |
_mm_cvtsi128_si32 |
✅ wasm_i32x4_extract_lane |
_mm_cvtsi128_si64 (_mm_cvtsi128_si64x) |
✅ wasm_i64x2_extract_lane |
_mm_cvtsi32_sd |
❌ skaliert |
_mm_cvtsi32_si128 |
💡 emuliert mit wasm_i32x4_make |
_mm_cvtsi64_sd (_mm_cvtsi64x_sd) |
❌ skaliert |
_mm_cvtsi64_si128 (_mm_cvtsi64x_si128) |
💡 emuliert mit wasm_i64x2_make |
_mm_cvtss_sd |
❌ skaliert |
_mm_cvttpd_epi32 |
❌ skaliert |
_mm_cvttps_epi32 |
❌ skaliert |
_mm_cvttsd_si32 |
❌ skaliert |
_mm_cvttsd_si64 (_mm_cvttsd_si64x) |
❌ skaliert |
_mm_div_pd |
✅ wasm_f64x2_div |
_mm_div_sd |
⚠️ emuliert mit einem Mischen |
_mm_extract_epi16 |
✅ wasm_u16x8_extract_lane |
_mm_insert_epi16 |
✅ wasm_i16x8_replace_lane |
_mm_lfence |
⚠️ Eine vollständige Barriere in Multithread-Builds. |
_mm_load_pd |
🟡 wasm_v128_load. VM muss Typ erraten. |
_mm_load1_pd (_mm_load_pd1) |
🟡 Virtuell. wasm_v64x2_load_splat, VM muss Typ erraten. |
_mm_load_sd |
❌ emuliert mit wasm_f64x2_make |
_mm_load_si128 |
🟡 wasm_v128_load. VM muss Typ erraten. |
_mm_loadh_pd |
❌ Keine Wasm SIMD Unterstützung. |
_mm_loadl_epi64 |
❌ Keine Wasm SIMD Unterstützung. |
_mm_loadl_pd |
❌ Keine Wasm SIMD Unterstützung. |
_mm_loadr_pd |
💡 Virtuell. Simd-Laden + Mischen. |
_mm_loadu_pd |
🟡 wasm_v128_load. VM muss Typ erraten. |
_mm_loadu_si128 |
🟡 wasm_v128_load. VM muss Typ erraten. |
_mm_loadu_si64 |
❌ emuliert mit const+skalarer Laden+Lane ersetzen |
_mm_loadu_si32 |
❌ emuliert mit const+skalarer Laden+Lane ersetzen |
_mm_loadu_si16 |
❌ emuliert mit const+skalarer Laden+Lane ersetzen |
_mm_madd_epi16 |
✅ wasm_i32x4_dot_i16x8 |
_mm_maskmoveu_si128 |
❌ skaliert |
_mm_max_epi16 |
✅ wasm_i16x8_max |
_mm_max_epu8 |
✅ wasm_u8x16_max |
_mm_max_pd |
TODO: zu wasm_f64x2_pmax migrieren |
_mm_max_sd |
⚠️ emuliert mit einem Mischen |
_mm_mfence |
⚠️ Eine vollständige Barriere in Multithread-Builds. |
_mm_min_epi16 |
✅ wasm_i16x8_min |
_mm_min_epu8 |
✅ wasm_u8x16_min |
_mm_min_pd |
TODO: zu wasm_f64x2_pmin migrieren |
_mm_min_sd |
⚠️ emuliert mit einem Mischen |
_mm_move_epi64 |
💡 emuliert mit einem Mischen. VM muss Typ erraten. |
_mm_move_sd |
💡 emuliert mit einem Mischen. VM muss Typ erraten. |
_mm_movemask_epi8 |
✅ wasm_i8x16_bitmask |
_mm_movemask_pd |
✅ wasm_i64x2_bitmask |
_mm_mul_epu32 |
⚠️ emuliert mit wasm_u64x2_extmul_low_u32x4 + 2 shuffles |
_mm_mul_pd |
✅ wasm_f64x2_mul |
_mm_mul_sd |
⚠️ emuliert mit einem Mischen |
_mm_mulhi_epi16 |
⚠️ emuliert mit 2x SIMD extmul+generischem Mischen |
_mm_mulhi_epu16 |
⚠️ emuliert mit 2x SIMD extmul+generischem Mischen |
_mm_mullo_epi16 |
✅ wasm_i16x8_mul |
_mm_or_pd |
🟡 wasm_v128_or. VM muss Typ erraten. |
_mm_or_si128 |
🟡 wasm_v128_or. VM muss Typ erraten. |
_mm_packs_epi16 |
✅ wasm_i8x16_narrow_i16x8 |
_mm_packs_epi32 |
✅ wasm_i16x8_narrow_i32x4 |
_mm_packus_epi16 |
✅ wasm_u8x16_narrow_i16x8 |
_mm_pause |
💭 No-Op. |
_mm_sad_epu8 |
⚠️ emuliert mit elf SIMD-Anweisungen + 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 |
💡 emuliert mit 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 |
💡 emuliert mit wasm_f64x2_const |
_mm_setzero_si128 |
💡 emuliert mit wasm_i64x2_const |
_mm_shuffle_epi32 |
💡 emuliert mit einem allgemeinen Mischen |
_mm_shuffle_pd |
💡 emuliert mit einem allgemeinen Mischen |
_mm_shufflehi_epi16 |
💡 emuliert mit einem allgemeinen Mischen |
_mm_shufflelo_epi16 |
💡 emuliert mit einem allgemeinen Mischen |
_mm_sll_epi16 |
❌ skaliert |
_mm_sll_epi32 |
❌ skaliert |
_mm_sll_epi64 |
❌ skaliert |
_mm_slli_epi16 |
💡 wasm_i16x8_shl |
_mm_slli_epi32 |
💡 wasm_i32x4_shl |
_mm_slli_epi64 |
💡 wasm_i64x2_shl |
_mm_slli_si128 (_mm_bslli_si128) |
💡 emuliert mit einem allgemeinen Mischen |
_mm_sqrt_pd |
✅ wasm_f64x2_sqrt |
_mm_sqrt_sd |
⚠️ emuliert mit einem Mischen |
_mm_sra_epi16 |
❌ skaliert |
_mm_sra_epi32 |
❌ skaliert |
_mm_srai_epi16 |
💡 wasm_i16x8_shr |
_mm_srai_epi32 |
💡 wasm_i32x4_shr |
_mm_srl_epi16 |
❌ skaliert |
_mm_srl_epi32 |
❌ skaliert |
_mm_srl_epi64 |
❌ skaliert |
_mm_srli_epi16 |
💡 wasm_u16x8_shr |
_mm_srli_epi32 |
💡 wasm_u32x4_shr |
_mm_srli_epi64 |
💡 wasm_u64x2_shr |
_mm_srli_si128 (_mm_bsrli_si128) |
💡 emuliert mit einem allgemeinen Mischen |
_mm_store_pd |
🟡 wasm_v128_store. VM muss Typ erraten. |
_mm_store_sd |
💡 emuliert mit skalarem Speichern |
_mm_store_si128 |
🟡 wasm_v128_store. VM muss Typ erraten. |
_mm_store1_pd (_mm_store_pd1) |
🟡 Virtuell. Emuliert mit Mischen. |
_mm_storeh_pd |
❌ Mischen + skalare Speicherungen |
_mm_storel_epi64 |
❌ skalarer Speicher |
_mm_storel_pd |
❌ skalarer Speicher |
_mm_storer_pd |
❌ Mischen + skalare Speicherungen |
_mm_storeu_pd |
🟡 wasm_v128_store. VM muss Typ erraten. |
_mm_storeu_si128 |
🟡 wasm_v128_store. VM muss Typ erraten. |
_mm_storeu_si64 |
💡 emuliert mit extract lane+scalar store |
_mm_storeu_si32 |
💡 emuliert mit extract lane+scalar store |
_mm_storeu_si16 |
💡 emuliert mit extract lane+scalar store |
_mm_stream_pd |
🟡 wasm_v128_store. VM muss Typ erraten. |
_mm_stream_si128 |
🟡 wasm_v128_store. VM muss Typ erraten. |
_mm_stream_si32 |
🟡 wasm_v128_store. VM muss Typ erraten. |
_mm_stream_si64 |
🟡 wasm_v128_store. VM muss Typ erraten. |
_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 |
⚠️ emuliert mit einem Mischen |
_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 |
❌ skaliert |
_mm_ucomige_sd |
❌ skaliert |
_mm_ucomigt_sd |
❌ skaliert |
_mm_ucomile_sd |
❌ skaliert |
_mm_ucomilt_sd |
❌ skaliert |
_mm_ucomineq_sd |
❌ skaliert |
_mm_undefined_pd |
✅ Virtuell |
_mm_undefined_si128 |
✅ Virtuell |
_mm_unpackhi_epi16 |
💡 emuliert mit einem Mischen |
_mm_unpackhi_epi32 |
💡 emuliert mit einem Mischen |
_mm_unpackhi_epi64 |
💡 emuliert mit einem Mischen |
_mm_unpackhi_epi8 |
💡 emuliert mit einem Mischen |
_mm_unpachi_pd |
💡 emuliert mit einem Mischen |
_mm_unpacklo_epi16 |
💡 emuliert mit einem Mischen |
_mm_unpacklo_epi32 |
💡 emuliert mit einem Mischen |
_mm_unpacklo_epi64 |
💡 emuliert mit einem Mischen |
_mm_unpacklo_epi8 |
💡 emuliert mit einem Mischen |
_mm_unpacklo_pd |
💡 emuliert mit einem Mischen |
_mm_xor_pd |
🟡 wasm_v128_xor. VM muss Typ erraten. |
_mm_xor_si128 |
🟡 wasm_v128_xor. VM muss Typ erraten. |
_mm_add_si64, _mm_movepi64_pi64, _mm_movpi64_epi64, _mm_mul_su32, _mm_sub_si64, _mm_cvtpd_pi32, _mm_cvtpi32_pd, _mm_cvttpd_pi32
Jeder Code, der auf diese intrinsischen Funktionen verweist, wird nicht kompiliert.
Die folgende Tabelle hebt die Verfügbarkeit und erwartete Leistung verschiedener SSE3-Intrinsics hervor. Siehe Intel Intrinsics Guide zu SSE3.
Intrinsischer Name |
WebAssembly SIMD-Unterstützung |
|---|---|
_mm_lddqu_si128 |
✅ wasm_v128_load. |
_mm_addsub_ps |
⚠️ emuliert mit einem SIMD Add+Mul+Const |
_mm_hadd_ps |
⚠️ emuliert mit einem SIMD Add+zwei Shuffles |
_mm_hsub_ps |
⚠️ emuliert mit einem SIMD Sub+zwei Shuffles |
_mm_movehdup_ps |
💡 emuliert mit einem allgemeinen Mischen |
_mm_moveldup_ps |
💡 emuliert mit einem allgemeinen Mischen |
_mm_addsub_pd |
⚠️ emuliert mit einem SIMD Add+Mul+Const |
_mm_hadd_pd |
⚠️ emuliert mit einem SIMD Add+zwei Shuffles |
_mm_hsub_pd |
⚠️ emuliert mit einem SIMD Add+zwei Shuffles |
_mm_loaddup_pd |
🟡 Virtuell. wasm_v64x2_load_splat, VM muss Typ erraten. |
_mm_movedup_pd |
💡 emuliert mit einem allgemeinen Mischen |
_MM_GET_DENORMALS_ZERO_MODE |
✅ Gibt immer _MM_DENORMALS_ZERO_ON zurück. D.h. Denormale sind verfügbar. |
_MM_SET_DENORMALS_ZERO_MODE |
⚫ Nicht verfügbar. Festgelegt auf _MM_DENORMALS_ZERO_ON. |
_mm_monitor |
⚫ Nicht verfügbar. |
_mm_mwait |
⚫ Nicht verfügbar. |
Die folgende Tabelle hebt die Verfügbarkeit und erwartete Leistung verschiedener SSSE3-Intrinsics hervor. Siehe Intel Intrinsics Guide zu SSSE3.
Intrinsischer Name |
WebAssembly SIMD-Unterstützung |
|---|---|
_mm_abs_epi8 |
✅ wasm_i8x16_abs |
_mm_abs_epi16 |
✅ wasm_i16x8_abs |
_mm_abs_epi32 |
✅ wasm_i32x4_abs |
_mm_alignr_epi8 |
⚠️ emuliert mit einem SIMD or+zwei Shifts |
_mm_hadd_epi16 |
⚠️ emuliert mit einem SIMD Add+zwei Shuffles |
_mm_hadd_epi32 |
⚠️ emuliert mit einem SIMD Add+zwei Shuffles |
_mm_hadds_epi16 |
⚠️ emuliert mit einem SIMD adds+zwei shuffles |
_mm_hsub_epi16 |
⚠️ emuliert mit einem SIMD Sub+zwei Shuffles |
_mm_hsub_epi32 |
⚠️ emuliert mit einem SIMD Sub+zwei Shuffles |
_mm_hsubs_epi16 |
⚠️ emuliert mit einem SIMD subs+zwei shuffles |
_mm_maddubs_epi16 |
⚠️ emuliert mit SIMD gesättigtem Add+vier Shifts+zwei Muls+And+Const |
_mm_mulhrs_epi16 |
⚠️ emuliert mit SIMD vier Widen+zwei Muls+vier Adds+komplexes Shuffle+Const |
_mm_shuffle_epi8 |
⚠️ emuliert mit einem SIMD swizzle+and+const |
_mm_sign_epi8 |
⚠️ emuliert mit SIMD zwei cmp+zwei logical+add |
_mm_sign_epi16 |
⚠️ emuliert mit SIMD zwei cmp+zwei logical+add |
_mm_sign_epi32 |
⚠️ emuliert mit SIMD zwei cmp+zwei logical+add |
_mm_abs_pi8, _mm_abs_pi16, _mm_abs_pi32, _mm_alignr_pi8, _mm_hadd_pi16, _mm_hadd_pi32, _mm_hadds_pi16, _mm_hsub_pi16, _mm_hsub_pi32, _mm_hsubs_pi16, _mm_maddubs_pi16, _mm_mulhrs_pi16, _mm_shuffle_pi8, _mm_sign_pi8, _mm_sign_pi16 und _mm_sign_pi32
Jeder Code, der auf diese intrinsischen Funktionen verweist, wird nicht kompiliert.
Die folgende Tabelle hebt die Verfügbarkeit und erwartete Leistung verschiedener SSE4.1-Intrinsics hervor. Siehe Intel Intrinsics Guide zu SSE4.1.
Intrinsischer Name |
WebAssembly SIMD-Unterstützung |
|---|---|
_mm_blend_epi16 |
💡 emuliert mit einem allgemeinen Mischen |
_mm_blend_pd |
💡 emuliert mit einem allgemeinen Mischen |
_mm_blend_ps |
💡 emuliert mit einem allgemeinen Mischen |
_mm_blendv_epi8 |
⚠️ emuliert mit einem SIMD shr+and+andnot+or |
_mm_blendv_pd |
⚠️ emuliert mit einem SIMD shr+and+andnot+or |
_mm_blendv_ps |
⚠️ emuliert mit einem SIMD shr+and+andnot+or |
_mm_ceil_pd |
✅ wasm_f64x2_ceil |
_mm_ceil_ps |
✅ wasm_f32x4_ceil |
_mm_ceil_sd |
⚠️ emuliert mit einem Mischen |
_mm_ceil_ss |
⚠️ emuliert mit einem Mischen |
_mm_cmpeq_epi64 |
⚠️ emuliert mit einem SIMD cmp+and+shuffle |
_mm_cvtepi16_epi32 |
✅ wasm_i32x4_widen_low_i16x8 |
_mm_cvtepi16_epi64 |
⚠️ emuliert mit einem SIMD widen+const+cmp+shuffle |
_mm_cvtepi32_epi64 |
⚠️ emuliert mit SIMD const+cmp+shuffle |
_mm_cvtepi8_epi16 |
✅ wasm_i16x8_widen_low_i8x16 |
_mm_cvtepi8_epi32 |
⚠️ emuliert mit zwei SIMD widens |
_mm_cvtepi8_epi64 |
⚠️ emuliert mit zwei SIMD widens+const+cmp+shuffle |
_mm_cvtepu16_epi32 |
✅ wasm_u32x4_extend_low_u16x8 |
_mm_cvtepu16_epi64 |
⚠️ emuliert mit SIMD const+zwei shuffles |
_mm_cvtepu32_epi64 |
⚠️ emuliert mit SIMD const+shuffle |
_mm_cvtepu8_epi16 |
✅ wasm_u16x8_extend_low_u8x16 |
_mm_cvtepu8_epi32 |
⚠️ emuliert mit zwei SIMD widens |
_mm_cvtepu8_epi64 |
⚠️ emuliert mit SIMD const+drei shuffles |
_mm_dp_pd |
⚠️ emuliert mit SIMD mul+add+setzero+2xblend |
_mm_dp_ps |
⚠️ emuliert mit 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 |
⚠️ emuliert mit einem Mischen |
_mm_floor_ss |
⚠️ emuliert mit einem Mischen |
_mm_insert_epi32 |
✅ wasm_i32x4_replace_lane |
_mm_insert_epi64 |
✅ wasm_i64x2_replace_lane |
_mm_insert_epi8 |
✅ wasm_i8x16_replace_lane |
_mm_insert_ps |
⚠️ emuliert mit generischen Non-SIMD-Mapping-Shuffles |
_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 |
💣 skaliert |
_mm_mpsadbw_epu8 |
💣 skaliert |
_mm_mul_epi32 |
⚠️ emuliert mit wasm_i64x2_extmul_low_i32x4 + 2 shuffles |
_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 |
⚠️ emuliert mit einem Mischen |
_mm_round_ss |
⚠️ emuliert mit einem Mischen |
_mm_stream_load_si128 |
🟡 wasm_v128_load. VM muss Typ erraten. |
_mm_test_all_ones |
❌ skaliert |
_mm_test_all_zeros |
❌ skaliert |
_mm_test_mix_ones_zeros |
❌ skaliert |
_mm_testc_si128 |
❌ skaliert |
_mm_testnzc_si128 |
❌ skaliert |
_mm_testz_si128 |
❌ skaliert |
Die folgende Tabelle hebt die Verfügbarkeit und erwartete Leistung verschiedener SSE4.2-Intrinsics hervor. Siehe Intel Intrinsics Guide zu SSE4.2.
Intrinsischer Name |
WebAssembly SIMD-Unterstützung |
|---|---|
_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
Jeder Code, der auf diese intrinsischen Funktionen verweist, wird nicht kompiliert.
Die folgende Tabelle hebt die Verfügbarkeit und erwartete Leistung verschiedener AVX-Intrinsics hervor. Siehe Intel Intrinsics Guide zu AVX.
Intrinsischer Name |
WebAssembly SIMD-Unterstützung |
|---|---|
_mm_broadcast_ss |
✅ wasm_v32x4_load_splat |
_mm_cmp_pd |
⚠️ emuliert mit 1-2 SIMD cmp+and/or |
_mm_cmp_ps |
⚠️ emuliert mit 1-2 SIMD cmp+and/or |
_mm_cmp_sd |
⚠️ emuliert mit 1-2 SIMD cmp+and/or+move |
_mm_cmp_ss |
⚠️ emuliert mit 1-2 SIMD cmp+and/or+move |
_mm_maskload_pd |
⚠️ emuliert mit SIMD load+shift+and |
_mm_maskload_ps |
⚠️ emuliert mit SIMD load+shift+and |
_mm_maskstore_pd |
❌ skaliert |
_mm_maskstore_ps |
❌ skaliert |
_mm_permute_pd |
💡 emuliert mit einem allgemeinen Mischen |
_mm_permute_ps |
💡 emuliert mit einem allgemeinen Mischen |
_mm_permutevar_pd |
💣 skaliert |
_mm_permutevar_ps |
💣 skaliert |
_mm_testc_pd |
💣 emuliert mit komplexer SIMD+Skalar-Sequenz |
_mm_testc_ps |
💣 emuliert mit komplexer SIMD+Skalar-Sequenz |
_mm_testnzc_pd |
💣 emuliert mit komplexer SIMD+Skalar-Sequenz |
_mm_testnzc_ps |
💣 emuliert mit komplexer SIMD+Skalar-Sequenz |
_mm_testz_pd |
💣 emuliert mit komplexer SIMD+Skalar-Sequenz |
_mm_testz_ps |
💣 emuliert mit komplexer SIMD+Skalar-Sequenz |
Es sind nur die 128-Bit breiten Anweisungen aus dem AVX-Befehlssatz aufgeführt. Die 256-Bit breiten AVX-Anweisungen werden durch zwei 128-Bit breite Anweisungen emuliert.
Emscripten unterstützt das Kompilieren bestehender Codebasen, die ARM NEON verwenden, indem die Direktive -mfpu=neon an den Compiler übergeben und der Header <arm_neon.h> eingebunden wird.
In Bezug auf die Leistung ist es sehr wichtig zu beachten, dass nur Anweisungen, die auf 128-Bit breiten Vektoren operieren, sauber unterstützt werden. Dies bedeutet, dass nahezu jede Anweisung, die keine „q“-Variante ist (d.h. „vaddq“ im Gegensatz zu „vadd“), skaliert wird.
Diese stammen aus dem SIMDe-Repository auf GitHub. Um Emscripten mit der neuesten SIMDe-Version zu aktualisieren, führen Sie tools/simde_update.py aus.
Die folgende Tabelle hebt die Verfügbarkeit verschiedener 128-Bit breiter Intrinsics hervor.
✅ Wasm SIMD hat einen nativen Opcode, der der NEON-Anweisung entspricht und native Leistung liefern sollte
💡 Obwohl die Wasm SIMD-Spezifikation keine ordnungsgemäße Leistungsgarantie bietet, sollte diese intrinsische Funktion bei einem ausreichend intelligenten Compiler und einem Laufzeit-VM-Pfad in der Lage sein, die identische native NEON-Anweisung zu generieren.
⚠️ Die zugrunde liegende NEON-Anweisung ist nicht verfügbar, wird aber über höchstens wenige andere Wasm SIMD-Anweisungen emuliert, was eine geringe Leistungsbeeinträchtigung verursacht.
❌ Die zugrunde liegende NEON-Anweisung wird von der Wasm SIMD-Spezifikation nicht offengelegt, daher muss sie über einen langsamen Pfad emuliert werden, z.B. eine Abfolge mehrerer langsamerer SIMD-Anweisungen oder eine skalare Implementierung.
⚫ Die angegebene NEON-Intrinsic ist nicht verfügbar. Das Referenzieren der Intrinsic führt zu einem Compilerfehler.
Für detaillierte Informationen zu jeder intrinsischen Funktion siehe die NEON Intrinsics Reference.
Den aktuellen Implementierungsstatus der NEON-Intrinsics finden Sie im SIMDe-Implementierungsstatus.
Intrinsischer Name |
Wasm SIMD-Unterstützung |
|---|---|
vaba |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vabaq |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vabal |
⚫ Nicht implementiert, löst Compilerfehler aus |
vabd |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vabdq |
✅ nativ |
vabdl |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vabs |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vabq |
✅ nativ |
vadd |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vaddq_s & vaddq_f |
✅ nativ |
vaddhn |
💡 Abhängig von einem ausreichend intelligenten Compiler, sollte aber nahezu nativ sein |
vaddl |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vaddlv |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vaddv |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vaddw |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vand |
✅ nativ |
vbcaxq |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vbic |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vbiq |
✅ nativ |
vbsl |
✅ nativ |
vcage |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vcagt |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vceq |
💡 Abhängig von einem ausreichend intelligenten Compiler, sollte aber nahezu nativ sein |
vceqz |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vcge |
✅ nativ |
vcgez |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vcgt |
✅ nativ |
vcgtz |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vcle |
✅ nativ |
vclez |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vcls |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vclt |
✅ nativ |
vcltz |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vcmla, vcmla_rot90, cmla_rot180, cmla_rot270 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vcmlq |
✅ nativ |
vcnt |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vclz |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vcombine |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vcreate |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vdot |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vdot_lane |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vdup |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vdup_n |
✅ nativ |
veor |
✅ nativ |
vext |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vfma, vfma_lane, vfma_n |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vget_lane |
✅ nativ |
vhadd |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vhsub |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vld1 |
✅ nativ |
vld2 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vld3 |
💡 Abhängig von einem ausreichend intelligenten Compiler, sollte aber nahezu nativ sein |
vld4 |
💡 Abhängig von einem ausreichend intelligenten Compiler, sollte aber nahezu nativ sein |
vld4_lane |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vmax |
✅ nativ |
vmaxv |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vmin |
✅ nativ |
vminv |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vmla |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vmlal |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vmlal_high_n |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vmlal_lane |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vmls |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vmls_n |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vmlsl |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vmlsl_high |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vmlsl_high_n |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vmlsl_lane |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vmovl |
✅ nativ |
vmul |
✅ nativ |
vmul_n |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vmull |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vmull_n |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vmull_lane |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vmull_high |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vmvn |
✅ nativ |
vneg |
✅ nativ |
vorn |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vorr |
✅ nativ |
vpadal |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vpadd |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vpaddl |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vpmax |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vpmin |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vpminnm |
⚫ Nicht implementiert, löst Compilerfehler aus |
vqabs |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqabsb |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqadd |
💡 Abhängig von einem ausreichend intelligenten Compiler, sollte aber nahezu nativ sein |
vqaddb |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqdmulh |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqdmulh_lane |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqneg |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqnegb |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqrdmulh |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqrdmulh_lane |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqshl |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqshlb |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqshrn_n |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqshrun_n |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqsub |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqsubb |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqtbl1 |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vqtbl2 |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vqtbl3 |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vqtbl4 |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vqtbx1 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqtbx2 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqtbx3 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vqtbx4 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vrbit |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vrecpe |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vrecps |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vreinterpret |
💡 Abhängig von einem ausreichend intelligenten Compiler, sollte aber nahezu nativ sein |
vrev16 |
✅ nativ |
vrev32 |
✅ nativ |
vrev64 |
✅ nativ |
vrhadd |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vrsh_n |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vrshn_n |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vrsqrte |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vrsqrts |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vrshl |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vrshr_n |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vrsra_n |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vset_lane |
✅ nativ |
vshl |
skaliert |
vshl_n |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vshll_n |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vshr_n |
⚠️ hat keine direkte Implementierung, wird aber mit schnellen Neon-Anweisungen emuliert |
vshrn_n |
⚠️ hat keine direkte Implementierung, wird aber mit schnellen Neon-Anweisungen emuliert |
vsqadd |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vsra_n |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vsri_n |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vst1 |
✅ nativ |
vst1_lane |
💡 Abhängig von einem ausreichend intelligenten Compiler, sollte aber nahezu nativ sein |
vst2 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vst2_lane |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vst3 |
💡 Abhängig von einem ausreichend intelligenten Compiler, sollte aber nahezu nativ sein |
vst3_lane |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vst4 |
💡 Abhängig von einem ausreichend intelligenten Compiler, sollte aber nahezu nativ sein |
vst4_lane |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vsub |
✅ nativ |
vsubl |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vsubl_high |
⚠️ Hat keine direkte Implementierung, wird aber mit schnellen NEON-Anweisungen emuliert |
vsubn |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vsubw |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vtbl1 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vtbl2 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vtbl3 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vtbl4 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vtbx1 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vtbx2 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vtbx3 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vtbx4 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vtrn |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vtrn1 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vtrn2 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vtst |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vuqadd |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vuqaddb |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vuzp |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vuzp1 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vuzp2 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vxar |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vzip |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vzip1 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |
vzip2 |
❌ Wird mit langsamen Anweisungen emuliert oder skaliert |