SIMD mit WebAssembly verwenden

Emscripten unterstützt die WebAssembly SIMD Funktion. Es gibt fünf verschiedene Möglichkeiten, WebAssembly SIMD in Ihren C/C++-Programmen zu nutzen

  1. Aktivieren Sie den LLVM/Clang SIMD Autovectorizer, um WebAssembly SIMD automatisch anzusprechen, ohne Änderungen am C/C++-Quellcode vornehmen zu müssen.

  2. Schreiben Sie SIMD-Code unter Verwendung der GCC/Clang SIMD Vector Extensions (__attribute__((vector_size(16))))

  3. Schreiben Sie SIMD-Code unter Verwendung der WebAssembly SIMD Intrinsics (#include <wasm_simd128.h>)

  4. Kompilieren Sie vorhandenen SIMD-Code, der die x86 SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2 oder AVX Intrinsics verwendet (#include <*mmintrin.h>)

  5. 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.

GCC/Clang SIMD Vector Extensions

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.

WebAssembly SIMD Intrinsics

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.

Einschränkungen und Verhaltensunterschiede

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.

Optimierungsüberlegungen

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-Instruktionen mit Leistungsbeeinträchtigungen

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.

Kompilieren von SIMD-Code, der auf x86 SSE*-Befehlssätze abzielt

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.

Die folgende Legende wird verwendet, um die erwartete Leistung verschiedener Anweisungen hervorzuheben
  • ✅ 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.

x86 SSE Intrinsics verfügbar über #include <xmmintrin.h> und -msse

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.
Nicht ausgerichtetes Laden auf x86 CPUs.

_mm_loadl_pi

❌ Keine Wasm SIMD Unterstützung.
Emuliert mit skalarem Laden + Mischen.

_mm_loadh_pi

❌ Keine Wasm SIMD Unterstützung.
Emuliert mit skalarem Laden + Mischen.

_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.
Nicht ausgerichtetes Speichern auf x86 CPUs.

_mm_stream_ps

🟡 wasm_v128_store. VM muss Typ erraten.
Keine Cache-Kontrolle in Wasm SIMD.

_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.
Nicht ausgerichtetes Speichern auf x86 CPUs.

_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.
Emuliert mit voller Präzisionsteilung. simd/#3

_mm_rcp_ss

❌ Keine Wasm SIMD Unterstützung.
Emuliert mit voller Präzisionsteilung + Mischen simd/#3

_mm_sqrt_ps

✅ wasm_f32x4_sqrt

_mm_sqrt_ss

⚠️ emuliert mit einem Mischen

_mm_rsqrt_ps

❌ Keine Wasm SIMD Unterstützung.
Emuliert mit voller Präzisionsteilung + sqrt. simd/#3

_mm_rsqrt_ss

❌ Keine Wasm SIMD Unterstützung.
Emuliert mit voller Präzisionsteilung + sqrt + Mischen. simd/#3

_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_ROUND_NEAREST | alle maskierten Ausnahmen (0x1f80).

_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

⚫ Die folgenden Erweiterungen, die der SSE1-Befehlssatz für 64-Bit breite MMX-Register mit sich brachte, sind nicht verfügbar
  • _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.

x86 SSE2 Intrinsics verfügbar über #include <emmintrin.h> und -msse2

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.
Nicht ausgerichtetes Laden auf x86 CPUs.

_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.
Nicht ausgerichtetes Laden auf x86 CPUs.

_mm_loadh_pd

❌ Keine Wasm SIMD Unterstützung.
Emuliert mit skalarem Laden + Mischen.

_mm_loadl_epi64

❌ Keine Wasm SIMD Unterstützung.
Emuliert mit skalarem Laden + Mischen.

_mm_loadl_pd

❌ Keine Wasm SIMD Unterstützung.
Emuliert mit skalarem Laden + Mischen.

_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
✅ wenn die Shift-Anzahl eine sofortige Konstante ist.

_mm_slli_epi32

💡 wasm_i32x4_shl
✅ wenn die Shift-Anzahl eine sofortige Konstante ist.

_mm_slli_epi64

💡 wasm_i64x2_shl
✅ wenn die Shift-Anzahl eine sofortige Konstante ist.

_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
✅ wenn die Shift-Anzahl eine sofortige Konstante ist.

_mm_srai_epi32

💡 wasm_i32x4_shr
✅ wenn die Shift-Anzahl eine sofortige Konstante ist.

_mm_srl_epi16

❌ skaliert

_mm_srl_epi32

❌ skaliert

_mm_srl_epi64

❌ skaliert

_mm_srli_epi16

💡 wasm_u16x8_shr
✅ wenn die Shift-Anzahl eine sofortige Konstante ist.

_mm_srli_epi32

💡 wasm_u32x4_shr
✅ wenn die Shift-Anzahl eine sofortige Konstante ist.

_mm_srli_epi64

💡 wasm_u64x2_shr
✅ wenn die Shift-Anzahl eine sofortige Konstante ist.

_mm_srli_si128 (_mm_bsrli_si128)

💡 emuliert mit einem allgemeinen Mischen

_mm_store_pd

🟡 wasm_v128_store. VM muss Typ erraten.
Nicht ausgerichtetes Speichern auf x86 CPUs.

_mm_store_sd

💡 emuliert mit skalarem Speichern

_mm_store_si128

🟡 wasm_v128_store. VM muss Typ erraten.
Nicht ausgerichtetes Speichern auf x86 CPUs.

_mm_store1_pd (_mm_store_pd1)

🟡 Virtuell. Emuliert mit Mischen.
Nicht ausgerichtetes Speichern auf x86 CPUs.

_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.
Keine Cache-Kontrolle in Wasm SIMD.

_mm_stream_si128

🟡 wasm_v128_store. VM muss Typ erraten.
Keine Cache-Kontrolle in Wasm SIMD.

_mm_stream_si32

🟡 wasm_v128_store. VM muss Typ erraten.
Keine Cache-Kontrolle in Wasm SIMD.

_mm_stream_si64

🟡 wasm_v128_store. VM muss Typ erraten.
Keine Cache-Kontrolle in Wasm SIMD.

_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.

⚫ Die folgenden Erweiterungen, die der SSE2-Befehlssatz für 64-Bit breite MMX-Register mit sich brachte, sind nicht verfügbar
  • _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.

x86 SSE3 Intrinsics verfügbar über #include <pmmintrin.h> und -msse3

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.

x86 SSSE3 Intrinsics verfügbar über #include <tmmintrin.h> und -mssse3

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

⚫ Die SSSE3-Funktionen, die 64-Bit breite MMX-Register betreffen, sind nicht verfügbar
  • _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.

x86 SSE4.1 Intrinsics verfügbar über #include <smmintrin.h> und -msse4.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.
Nicht ausgerichtetes Laden auf x86 CPUs.

_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.

x86 SSE4.2 Intrinsics verfügbar über #include <nmmintrin.h> und -msse4.2

Intrinsischer Name

WebAssembly SIMD-Unterstützung

_mm_cmpgt_epi64

✅ wasm_i64x2_gt

⚫ Die SSE4.2-Funktionen, die Stringvergleiche und CRC-Berechnungen betreffen, sind nicht verfügbar
  • _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.

x86 AVX Intrinsics verfügbar über #include <immintrin.h> und -mavx

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.

Kompilieren von SIMD-Code, der auf den ARM NEON-Befehlssatz abzielt

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.

Ähnlich wie oben wird die folgende Legende verwendet
  • ✅ 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.

NEON Intrinsics

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