Pohled pod kapotu formátu WebAssembly: SIMD (vektorové) operace, druhá část

Dnes
Doba čtení: 57 minut

Sdílet

Pracovníci rozebírají vnitřek PC
Autor: Shutterstock
Ve WebAssembly je definováno asi dvě stě různých SIMD (vektorových) instrukcí. Dnes si popíšeme vybrané vektorové instrukce, z nichž některé umožňují provádění i dosti složitých operací (zhruba na úrovni SSE2).

Obsah

1. Pohled pod kapotu formátu WebAssembly: SIMD (vektorové) operace, 2 část

2. Krátké připomenutí: instrukce určené pro součet vektorů

3. Aritmetické operace součtu, rozdílu a změny znaménka prováděné s celými vektory

4. Způsob překladu funkcí z demonstračního příkladu do instrukcí WebAssembly

5. Tabulka se všemi instrukcemi pro součet, rozdíl a změnu znaménka celých vektorů

6. Aritmetická operace součinu prvků vektorů

7. Překlad operace součinu prvků vektorů s využitím podporovaných instrukcí

8. Tabulka s instrukcemi pro součin odpovídajících si prvků vektorů

9. Způsob překladu součinu prvků vektorů typu byte

10. Aritmetická operace podílu prvků vektorů

11. Překlad operace podílu prvků vektorů s využitím podporovaných instrukcí

12. Tabulka s instrukcemi pro podíl odpovídajících si prvků vektorů

13. Instrukce pro konstrukci nového vektoru

14. Instrukce pro extrakci prvku i pro zpětný zápis prvku do vektoru

15. Překlad složitějších vektorových operací: výpočet skalárního součinu dvou vektorů

16. Výpočet druhé odmocniny prvků vektorů typu floatdouble

17. Instrukce sloužící pro porovnání odpovídajících si prvků vektorů

18. Tabulka se všemi doposud popsanými instrukcemi

19. Články o SIMD, které doposud na Rootu vyšly

20. Odkazy na Internetu

1. Pohled pod kapotu formátu WebAssembly: SIMD (vektorové) operace, druhá část

Na předchozí článek dnes navážeme. Popíšeme si totiž další SIMD (vektorové) instrukce, které jsou oficiálně popsány ve WebAssembly a jsou i podporovány většinou implementací tohoto virtuálního stroje (důležité je zejména to, že jsou podporovány všemi prohlížeči). Ve WebAssembly je přitom definováno přibližně 200 těchto instrukcí – zajímavé však je, že některé instrukce chybí (resp. chybí jejich varianty pro určité typy vektorů), takže instrukční soubor není z tohoto pohledu ortogonální.

Překlad všech dále uvedených demonstračních příkladů budeme provádět následující sekvenci příkazů. První dva příkazy slouží pro překlad do Web Assembly, poslední příkaz pak zpětným překladem ukáže, jaké instrukce jsou uloženy ve výsledném binárním souboru:

clang -Os -msimd128 --target=wasm32 -emit-llvm -c -S ${1}.c
llc -march=wasm32 -filetype=obj ${1}.ll -o ${1}.wasm
wasm-objdump -d ${1}.wasm

Programátoři, co nesnášíte BS, ale máte rádi business! Y Soft je česká firma s globálním dopadem (100+ zemí, 1M+ uživatelů a >100% meziroční růst). R&D úplně bez manažerů (130 developerů). Otevíráme 30 pozic pro Cloud a AI: Praha/Brno/Ostrava/remote. Zodpovědnost ano, mikro-management ne. Pojď někam, kde můžeš věci změnit.

Y Soft logo

2. Krátké připomenutí: instrukce určené pro součet vektorů

V předchozím článku jsme si kromě dalších informací popsali i instrukce, které zajišťují součet vektorů, tj. takové instrukce, jež provádí součet prvek po prvku. Těchto instrukcí je ve WebAssembly definováno celkem šest, což odpovídá i všem v současnosti podporovaným typům vektorů. Všechny instrukce určené pro součet vektorů jsou vypsány v následující tabulce. Z pohledu pojmenování instrukcí se jedná o „běžný“ součet (add), ovšem prováděný s hodnotami typu, které jsou uvedeny v prefixu názvu instrukce (což je pro WebAssembly typické):

Operační kód Jméno instrukce Struktura vektorů, které se sčítají
fd 6e i8×16.add 16 prvků typu byte (16×8 bitů)
fd 8e 01 i16×8.add 8 prvků typu word (8×16 bitů)
fd ae 01 i32×4.add 4 prvky typu double word (4×32 bitů)
fd ce 01 i64×2.add 2 prvky typu quad word (2×64 bitů)
fd e4 01 f32×4.add 4 prvky typu single (4×32 bitů)
fd f0 01 f64×2.add 2 prvky typu double (2×64 bitů)
Poznámka: povšimněte si, že všechny tyto vektorové instrukce začínají prefixem 0×fd, což je prefix použitý pro naprostou většinu vektorových instrukcí. Dále je zajímavé, že operační kód instrukce pro součet vektorů, které obsahují prvky typu byte, má délku dvou bajtů (včetně již zmíněného prefixu), zatímco u ostatních čtyř typů vektorů je instrukční kód o bajt delší.

3. Aritmetické operace součtu, rozdílu a změny znaménka prováděné s celými vektory

Kromě součtu vektorů nabízí WebAssembly i možnost provádění dalších operací s odpovídajícími si prvky dvou vektorů nebo s prvky jediného vektoru. Nabídka těchto operací se liší podle toho, zda vektory obsahují celočíselné hodnoty nebo hodnoty s plovoucí řádovou čárkou, ovšem nezávisle na typu prvků jsou tři operace společné a plně podporované: součet vektorů (ten již známe), rozdíl vektorů a otočení znaménka u všech prvků jednoho vektoru.

V dnešním prvním demonstračním příkladu si vyzkoušíme, jakým způsobem jsou tyto operace přeloženy do instrukcí WebAssembly. V příkladu nejdříve nadefinovány všechny podporované typy vektorů, tj. vektory se šestnácti prvky typu bajt (byte) až po vektory se dvěma prvky typu double (ovšem šířka vektorů je stále 128 bitů):

typedef   signed char           i8x16 __attribute__((vector_size(16)));
typedef unsigned char           u8x16 __attribute__((vector_size(16)));
typedef   signed short int      i16x8 __attribute__((vector_size(16)));
typedef unsigned short int      u16x8 __attribute__((vector_size(16)));
typedef   signed int            i32x4 __attribute__((vector_size(16)));
typedef unsigned int            u32x4 __attribute__((vector_size(16)));
typedef   signed long long int  i64x2 __attribute__((vector_size(16)));
typedef unsigned long long int  u64x2 __attribute__((vector_size(16)));
typedef float                   f32x4 __attribute__((vector_size(16)));
typedef double                  f64x2 __attribute__((vector_size(16)));

Následně, abychom nemuseli stále opisovat prakticky stejný kód, jsou jednotlivé operace nadefinovány formou maker:

#define NEG(type) type neg_##type(type x) {return -x;}
#define ADD(type) type add_##type(type x, type y) {return x+y;}
#define SUB(type) type sub_##type(type x, type y) {return x-y;}

Každé makro je expandováno na funkci, která ve svém jménu obsahuje příslušný typ. Pokud například zavoláme první makro s typem i8×16, provede se expanze na:

i8x16 neg_i8x16(i8x16 x)
{
    return -x;
}

Dále si vytvoříme další pomocné makro, které se expanduje na sekvenci tří předchozích maker. Zavoláním tohoto makra dosáhneme expanzi do třiceti funkcí:

#define ALL(type) \
    NEG(type) \
    ADD(type) \
    SUB(type)
 
ALL(i8x16)
ALL(u8x16)
ALL(i16x8)
ALL(u16x8)
ALL(i32x4)
ALL(u32x4)
ALL(i64x2)
ALL(u64x2)
ALL(f32x4)
ALL(f64x2)
Poznámka: makrosystém jazyka C sice můžeme považovat za nebezpečný, ale může být velmi užitečný.

Celý zdrojový kód dnešního prvního demonstračního příkladu vypadá následovně:

typedef   signed char           i8x16 __attribute__((vector_size(16)));
typedef unsigned char           u8x16 __attribute__((vector_size(16)));
typedef   signed short int      i16x8 __attribute__((vector_size(16)));
typedef unsigned short int      u16x8 __attribute__((vector_size(16)));
typedef   signed int            i32x4 __attribute__((vector_size(16)));
typedef unsigned int            u32x4 __attribute__((vector_size(16)));
typedef   signed long long int  i64x2 __attribute__((vector_size(16)));
typedef unsigned long long int  u64x2 __attribute__((vector_size(16)));
typedef float                   f32x4 __attribute__((vector_size(16)));
typedef double                  f64x2 __attribute__((vector_size(16)));
 
#define NEG(type) type neg_##type(type x) {return -x;}
#define ADD(type) type add_##type(type x, type y) {return x+y;}
#define SUB(type) type sub_##type(type x, type y) {return x-y;}
 
#define ALL(type) \
    NEG(type) \
    ADD(type) \
    SUB(type)
 
ALL(i8x16)
ALL(u8x16)
ALL(i16x8)
ALL(u16x8)
ALL(i32x4)
ALL(u32x4)
ALL(i64x2)
ALL(u64x2)
ALL(f32x4)
ALL(f64x2)

4. Způsob překladu funkcí z demonstračního příkladu do instrukcí WebAssembly

Po překladu demonstračního příkladu z předchozí kapitoly z céčka do WebAssembly můžeme použít již dříve popsaný nástroj wasm-objdump pro zpětný překlad z bajtkódu do čitelné sekvence (strojových) instrukcí. Získat bychom měli zpětný překlad celkem třiceti funkcí, protože jsme (přes makra) vygenerovali tři céčkovské funkce pro každý z deseti datových typů.

Všechny přeložené funkce mají podobnou strukturu. Pro funkce s jediným parametrem vypadá tato struktura následovně:

func[0] <jméno_operace_jméno_datového_typu>:
  local.get 0
  ... příslušná vektorová operace ...
  end

Naopak pro funkce se dvěma parametry se struktura nepatrně změní do podoby:

func[0] <jméno_operace_jméno_datového_typu>:
  local.get 0
  local.get 1
  ... příslušná vektorová operace ...
  end
Poznámka: to ovšem znamená, že i když jsou do přeložených funkcí předávány vektory, je bajtkód do značné míry shodný s funkcemi, do kterých by se předávaly skalární hodnoty (či jen jedna hodnota).

Podívejme se nyní na výsledek překladu:

simd_B.wasm:    file format wasm 0x1
 
Code Disassembly:
 
000065 func[0] <neg_i8x16>:
 000066: 20 00                      | local.get 0
 000068: fd 61                      | i8x16.neg
 00006a: 0b                         | end
 
00006c func[1] <add_i8x16>:
 00006d: 20 01                      | local.get 1
 00006f: 20 00                      | local.get 0
 000071: fd 6e                      | i8x16.add
 000073: 0b                         | end
 
000075 func[2] <sub_i8x16>:
 000076: 20 00                      | local.get 0
 000078: 20 01                      | local.get 1
 00007a: fd 71                      | i8x16.sub
 00007c: 0b                         | end
 
00007e func[3] <neg_u8x16>:
 00007f: 20 00                      | local.get 0
 000081: fd 61                      | i8x16.neg
 000083: 0b                         | end
 
000085 func[4] <add_u8x16>:
 000086: 20 01                      | local.get 1
 000088: 20 00                      | local.get 0
 00008a: fd 6e                      | i8x16.add
 00008c: 0b                         | end
 
00008e func[5] <sub_u8x16>:
 00008f: 20 00                      | local.get 0
 000091: 20 01                      | local.get 1
 000093: fd 71                      | i8x16.sub
 000095: 0b                         | end
 
000097 func[6] <neg_i16x8>:
 000098: 20 00                      | local.get 0
 00009a: fd 81 01                   | i16x8.neg
 00009d: 0b                         | end
 
00009f func[7] <add_i16x8>:
 0000a0: 20 01                      | local.get 1
 0000a2: 20 00                      | local.get 0
 0000a4: fd 8e 01                   | i16x8.add
 0000a7: 0b                         | end
 
0000a9 func[8] <sub_i16x8>:
 0000aa: 20 00                      | local.get 0
 0000ac: 20 01                      | local.get 1
 0000ae: fd 91 01                   | i16x8.sub
 0000b1: 0b                         | end
 
0000b3 func[9] <neg_u16x8>:
 0000b4: 20 00                      | local.get 0
 0000b6: fd 81 01                   | i16x8.neg
 0000b9: 0b                         | end
 
0000bb func[10] <add_u16x8>:
 0000bc: 20 01                      | local.get 1
 0000be: 20 00                      | local.get 0
 0000c0: fd 8e 01                   | i16x8.add
 0000c3: 0b                         | end
 
0000c5 func[11] <sub_u16x8>:
 0000c6: 20 00                      | local.get 0
 0000c8: 20 01                      | local.get 1
 0000ca: fd 91 01                   | i16x8.sub
 0000cd: 0b                         | end
 
0000cf func[12] <neg_i32x4>:
 0000d0: 20 00                      | local.get 0
 0000d2: fd a1 01                   | i32x4.neg
 0000d5: 0b                         | end
 
0000d7 func[13] <add_i32x4>:
 0000d8: 20 01                      | local.get 1
 0000da: 20 00                      | local.get 0
 0000dc: fd ae 01                   | i32x4.add
 0000df: 0b                         | end
 
0000e1 func[14] <sub_i32x4>:
 0000e2: 20 00                      | local.get 0
 0000e4: 20 01                      | local.get 1
 0000e6: fd b1 01                   | i32x4.sub
 0000e9: 0b                         | end
 
0000eb func[15] <neg_u32x4>:
 0000ec: 20 00                      | local.get 0
 0000ee: fd a1 01                   | i32x4.neg
 0000f1: 0b                         | end
 
0000f3 func[16] <add_u32x4>:
 0000f4: 20 01                      | local.get 1
 0000f6: 20 00                      | local.get 0
 0000f8: fd ae 01                   | i32x4.add
 0000fb: 0b                         | end
 
0000fd func[17] <sub_u32x4>:
 0000fe: 20 00                      | local.get 0
 000100: 20 01                      | local.get 1
 000102: fd b1 01                   | i32x4.sub
 000105: 0b                         | end
 
000107 func[18] <neg_i64x2>:
 000108: 20 00                      | local.get 0
 00010a: fd c1 01                   | i64x2.neg
 00010d: 0b                         | end
 
00010f func[19] <add_i64x2>:
 000110: 20 01                      | local.get 1
 000112: 20 00                      | local.get 0
 000114: fd ce 01                   | i64x2.add
 000117: 0b                         | end
 
000119 func[20] <sub_i64x2>:
 00011a: 20 00                      | local.get 0
 00011c: 20 01                      | local.get 1
 00011e: fd d1 01                   | i64x2.sub
 000121: 0b                         | end
 
000123 func[21] <neg_u64x2>:
 000124: 20 00                      | local.get 0
 000126: fd c1 01                   | i64x2.neg
 000129: 0b                         | end
 
00012b func[22] <add_u64x2>:
 00012c: 20 01                      | local.get 1
 00012e: 20 00                      | local.get 0
 000130: fd ce 01                   | i64x2.add
 000133: 0b                         | end
 
000135 func[23] <sub_u64x2>:
 000136: 20 00                      | local.get 0
 000138: 20 01                      | local.get 1
 00013a: fd d1 01                   | i64x2.sub
 00013d: 0b                         | end
 
00013f func[24] <neg_f32x4>:
 000140: 20 00                      | local.get 0
 000142: fd e1 01                   | f32x4.neg
 000145: 0b                         | end
 
000147 func[25] <add_f32x4>:
 000148: 20 00                      | local.get 0
 00014a: 20 01                      | local.get 1
 00014c: fd e4 01                   | f32x4.add
 00014f: 0b                         | end
 
000151 func[26] <sub_f32x4>:
 000152: 20 00                      | local.get 0
 000154: 20 01                      | local.get 1
 000156: fd e5 01                   | f32x4.sub
 000159: 0b                         | end
 
00015b func[27] <neg_f64x2>:
 00015c: 20 00                      | local.get 0
 00015e: fd ed 01                   | f64x2.neg
 000161: 0b                         | end
 
000163 func[28] <add_f64x2>:
 000164: 20 00                      | local.get 0
 000166: 20 01                      | local.get 1
 000168: fd f0 01                   | f64x2.add
 00016b: 0b                         | end
 
00016d func[29] <sub_f64x2>:
 00016e: 20 00                      | local.get 0
 000170: 20 01                      | local.get 1
 000172: fd f1 01                   | f64x2.sub
 000175: 0b                         | end

5. Tabulka se všemi instrukcemi pro součet, rozdíl a změnu znaménka celých vektorů

Všechny doposud popsané vektorové instrukce jsou shrnuty v následující tabulce. Opět je z ní patrné použití prefixu 0×FD a taktéž to, že operace s vektory s prvky typu bajt mají kratší operační kód:

Operační kód Jméno instrukce Operace Struktura vektorů
fd 61 i8×16.neg otočení znaménka 16 prvků typu byte (16×8 bitů)
fd 6e i8×16.add součet vektorů 16 prvků typu byte (16×8 bitů)
fd 71 i8×16.sub rozdíl vektorů 16 prvků typu byte (16×8 bitů)
       
fd 81 01 i16×8.neg otočení znaménka 8 prvků typu word (8×16 bitů)
fd 8e 01 i16×8.add součet vektorů 8 prvků typu word (8×16 bitů)
fd 91 01 i16×8.sub rozdíl vektorů 8 prvků typu word (8×16 bitů)
       
fd a1 01 i32×4.neg otočení znaménka 4 prvky typu double word (4×32 bitů)
fd ae 01 i32×4.add součet vektorů 4 prvky typu double word (4×32 bitů)
fd b1 01 i32×4.sub rozdíl vektorů 4 prvky typu double word (4×32 bitů)
       
fd c1 01 i64×2.neg otočení znaménka 2 prvky typu quat word (2×64 bitů)
fd ce 01 i64×2.add součet vektorů 2 prvky typu quat word (2×64 bitů)
fd d1 01 i64×2.sub rozdíl vektorů 2 prvky typu quat word (2×64 bitů)
       
fd e1 01 f32×4.neg otočení znaménka 4 prvky typu single (4×32 bitů)
fd e4 01 f32×4.add součet vektorů 4 prvky typu single (4×32 bitů)
fd e5 01 f32×4.sub rozdíl vektorů 4 prvky typu single (4×32 bitů)
       
fd ed 01 f64×2.neg otočení znaménka 2 prvky typu double (2×64 bitů)
fd f0 01 f64×2.add součet vektorů 2 prvky typu double (2×64 bitů)
fd f1 01 f64×2.sub rozdíl vektorů 2 prvky typu double (2×64 bitů)

6. Aritmetická operace součinu prvků vektorů

Další typickou aritmetickou operací je operace součinu odpovídajících si prvků vektorů (tedy nikoli vektorový součin). Tuto operaci vysvětlujeme v samostatné kapitole z toho důvodu, že není podporovaná pro všechny typy vektorů a z tohoto důvodu musí překladače do WebAssembly používat „rozepsání“ této operace do poměrně dlouhé sekvence instrukcí. Tuto vlastnost WebAssembly je nutné znát, protože v opačném případě by se mohlo stát, že přepisem algoritmu do vektorové podoby by se rychlost výsledného programu mohla (paradoxně) snížit.

Součin prvků vektorů prvek po prvku si otestujeme na příkladu, který vznikl nepatrnou úpravou původního demonstračního příkladu:

typedef signed   char           i8x16 __attribute__((vector_size(16)));
typedef unsigned char           u8x16 __attribute__((vector_size(16)));
typedef signed   short int      i16x8 __attribute__((vector_size(16)));
typedef unsigned short int      u16x8 __attribute__((vector_size(16)));
typedef signed   int            i32x4 __attribute__((vector_size(16)));
typedef unsigned int            u32x4 __attribute__((vector_size(16)));
typedef   signed long long int  i64x2 __attribute__((vector_size(16)));
typedef unsigned long long int  u64x2 __attribute__((vector_size(16)));
typedef float                   f32x4 __attribute__((vector_size(16)));
typedef double                  f64x2 __attribute__((vector_size(16)));
 
#define MUL(type) type mul_##type(type x, type y) {return (type)(x*y);}
 
#define ALL(type) \
    MUL(type)
 
ALL(i8x16)
ALL(u8x16)
ALL(i16x8)
ALL(u16x8)
ALL(i32x4)
ALL(u32x4)
ALL(i64x2)
ALL(u64x2)
ALL(f32x4)
ALL(f64x2)

7. Překlad operace součinu prvků vektorů s využitím podporovaných instrukcí

Výsledek zpětného překladu z bajtkódu WebAssembly do čitelné podoby je poměrně rozsáhlý – jeho celková velikost dosahuje dvanácti kilobajtů! Je tomu tak z toho důvodu, že násobení vektorů prvek po prvku není podporováno pro všechny typy vektorů. Ukažme si prozatím výsledky pro podporované typy vektorů (a tedy pro existující instrukce WebAssembly):

000212 func[2] <mul_i16x8>:
 000213: 20 01                      | local.get 1
 000215: 20 00                      | local.get 0
 000217: fd 95 01                   | i16x8.mul
 00021a: 0b                         | end
 
00021c func[3] <mul_u16x8>:
 00021d: 20 01                      | local.get 1
 00021f: 20 00                      | local.get 0
 000221: fd 95 01                   | i16x8.mul
 000224: 0b                         | end
 
000226 func[4] <mul_i32x4>:
 000227: 20 01                      | local.get 1
 000229: 20 00                      | local.get 0
 00022b: fd b5 01                   | i32x4.mul
 00022e: 0b                         | end
 
000230 func[5] <mul_u32x4>:
 000231: 20 01                      | local.get 1
 000233: 20 00                      | local.get 0
 000235: fd b5 01                   | i32x4.mul
 000238: 0b                         | end
 
00023a func[6] <mul_i64x2>:
 00023b: 20 01                      | local.get 1
 00023d: 20 00                      | local.get 0
 00023f: fd d5 01                   | i64x2.mul
 000242: 0b                         | end
 
000244 func[7] <mul_u64x2>:
 000245: 20 01                      | local.get 1
 000247: 20 00                      | local.get 0
 000249: fd d5 01                   | i64x2.mul
 00024c: 0b                         | end
 
00024e func[8] <mul_f32x4>:
 00024f: 20 00                      | local.get 0
 000251: 20 01                      | local.get 1
 000253: fd e6 01                   | f32x4.mul
 000256: 0b                         | end
 
000258 func[9] <mul_f64x2>:
 000259: 20 00                      | local.get 0
 00025b: 20 01                      | local.get 1
 00025d: fd f2 01                   | f64x2.mul
 000260: 0b                         | end

8. Tabulka s instrukcemi pro součin odpovídajících si prvků vektorů

Všechny instrukce součinu prvků vektorů implementované ve WebAssembly jsou vypsány v následující tabulce. Opět zde můžeme vidět využití instrukčního prefixu fd a operační kódy instrukcí mají stejnou délku tří bajtů:

Operační kód Jméno instrukce Struktura vektorů, které se násobí
fd 95 01 i16×8.mul 8 prvků typu word (8×16 bitů)
fd b5 01 i32×4.mul 4 prvky typu double word (4×32 bitů)
fd d5 01 i64×2.mul 2 prvky typu quad word (2×64 bitů)
fd e6 01 f32×4.mul 4 prvky typu single (4×32 bitů)
fd f2 01 f64×2.mul 2 prvky typu double (2×64 bitů)

9. Způsob překladu součinu prvků vektorů typu byte

V případě, že se pokusíme o realizaci vynásobení odpovídajících si prvků vektorů typu byte, bude výsledek překladu do bajtkódu WebAssembly vypadat zcela odlišně od překladu, který jsme mohli vidět v sedmé kapitole. Je tomu tak z toho důvodu, že chybí příslušná SIMD instrukce, která by se měla jmenovat i8×16.mul. Překladač tedy musí provádět násobení skutečně prvek po prvku, což nebude příliš rychlé. Ve výsledném bajtkódu můžeme vidět opakující se sekvenci instrukcí:

local.get 1
i8x16.extract_lane_u n (pro n=1..15)
local.get 0
i8x16.extract_lane_u n (pro n=1..15)
i32.mul
i8x16.replace_lane n (pro n=1..15)

Výjimkou je vynásobení nejnižších prvků, kde se namísto replace_lane použije instrukce splat:

local.get 1
i8x16.extract_lane_u 0
local.get 0
i8x16.extract_lane_u 0
i32.mul
i8x16.splat
Poznámka: instrukce extract_lane, replace_lane a splat budou vysvětleny ve čtrnácté kapitole.

Celá funkce mul_i8×16 přeložená do bajtkódu bude vypadat následovně:

00004d func[0] <mul_i8x16>:
 00004e: 20 01                      | local.get 1
 000050: fd 16 00                   | i8x16.extract_lane_u 0
 000053: 20 00                      | local.get 0
 000055: fd 16 00                   | i8x16.extract_lane_u 0
 000058: 6c                         | i32.mul
 000059: fd 0f                      | i8x16.splat
 00005b: 20 01                      | local.get 1
 00005d: fd 16 01                   | i8x16.extract_lane_u 1
 000060: 20 00                      | local.get 0
 000062: fd 16 01                   | i8x16.extract_lane_u 1
 000065: 6c                         | i32.mul
 000066: fd 17 01                   | i8x16.replace_lane 1
 000069: 20 01                      | local.get 1
 00006b: fd 16 02                   | i8x16.extract_lane_u 2
 00006e: 20 00                      | local.get 0
 000070: fd 16 02                   | i8x16.extract_lane_u 2
 000073: 6c                         | i32.mul
 000074: fd 17 02                   | i8x16.replace_lane 2
 000077: 20 01                      | local.get 1
 000079: fd 16 03                   | i8x16.extract_lane_u 3
 00007c: 20 00                      | local.get 0
 00007e: fd 16 03                   | i8x16.extract_lane_u 3
 000081: 6c                         | i32.mul
 000082: fd 17 03                   | i8x16.replace_lane 3
 000085: 20 01                      | local.get 1
 000087: fd 16 04                   | i8x16.extract_lane_u 4
 00008a: 20 00                      | local.get 0
 00008c: fd 16 04                   | i8x16.extract_lane_u 4
 00008f: 6c                         | i32.mul
 000090: fd 17 04                   | i8x16.replace_lane 4
 000093: 20 01                      | local.get 1
 000095: fd 16 05                   | i8x16.extract_lane_u 5
 000098: 20 00                      | local.get 0
 00009a: fd 16 05                   | i8x16.extract_lane_u 5
 00009d: 6c                         | i32.mul
 00009e: fd 17 05                   | i8x16.replace_lane 5
 0000a1: 20 01                      | local.get 1
 0000a3: fd 16 06                   | i8x16.extract_lane_u 6
 0000a6: 20 00                      | local.get 0
 0000a8: fd 16 06                   | i8x16.extract_lane_u 6
 0000ab: 6c                         | i32.mul
 0000ac: fd 17 06                   | i8x16.replace_lane 6
 0000af: 20 01                      | local.get 1
 0000b1: fd 16 07                   | i8x16.extract_lane_u 7
 0000b4: 20 00                      | local.get 0
 0000b6: fd 16 07                   | i8x16.extract_lane_u 7
 0000b9: 6c                         | i32.mul
 0000ba: fd 17 07                   | i8x16.replace_lane 7
 0000bd: 20 01                      | local.get 1
 0000bf: fd 16 08                   | i8x16.extract_lane_u 8
 0000c2: 20 00                      | local.get 0
 0000c4: fd 16 08                   | i8x16.extract_lane_u 8
 0000c7: 6c                         | i32.mul
 0000c8: fd 17 08                   | i8x16.replace_lane 8
 0000cb: 20 01                      | local.get 1
 0000cd: fd 16 09                   | i8x16.extract_lane_u 9
 0000d0: 20 00                      | local.get 0
 0000d2: fd 16 09                   | i8x16.extract_lane_u 9
 0000d5: 6c                         | i32.mul
 0000d6: fd 17 09                   | i8x16.replace_lane 9
 0000d9: 20 01                      | local.get 1
 0000db: fd 16 0a                   | i8x16.extract_lane_u 10
 0000de: 20 00                      | local.get 0
 0000e0: fd 16 0a                   | i8x16.extract_lane_u 10
 0000e3: 6c                         | i32.mul
 0000e4: fd 17 0a                   | i8x16.replace_lane 10
 0000e7: 20 01                      | local.get 1
 0000e9: fd 16 0b                   | i8x16.extract_lane_u 11
 0000ec: 20 00                      | local.get 0
 0000ee: fd 16 0b                   | i8x16.extract_lane_u 11
 0000f1: 6c                         | i32.mul
 0000f2: fd 17 0b                   | i8x16.replace_lane 11
 0000f5: 20 01                      | local.get 1
 0000f7: fd 16 0c                   | i8x16.extract_lane_u 12
 0000fa: 20 00                      | local.get 0
 0000fc: fd 16 0c                   | i8x16.extract_lane_u 12
 0000ff: 6c                         | i32.mul
 000100: fd 17 0c                   | i8x16.replace_lane 12
 000103: 20 01                      | local.get 1
 000105: fd 16 0d                   | i8x16.extract_lane_u 13
 000108: 20 00                      | local.get 0
 00010a: fd 16 0d                   | i8x16.extract_lane_u 13
 00010d: 6c                         | i32.mul
 00010e: fd 17 0d                   | i8x16.replace_lane 13
 000111: 20 01                      | local.get 1
 000113: fd 16 0e                   | i8x16.extract_lane_u 14
 000116: 20 00                      | local.get 0
 000118: fd 16 0e                   | i8x16.extract_lane_u 14
 00011b: 6c                         | i32.mul
 00011c: fd 17 0e                   | i8x16.replace_lane 14
 00011f: 20 01                      | local.get 1
 000121: fd 16 0f                   | i8x16.extract_lane_u 15
 000124: 20 00                      | local.get 0
 000126: fd 16 0f                   | i8x16.extract_lane_u 15
 000129: 6c                         | i32.mul
 00012a: fd 17 0f                   | i8x16.replace_lane 15
 00012d: 0b                         | end

10. Aritmetická operace podílu prvků vektorů

Z pohledu implementace ve WebAssembly je nejvíce problematická operace podílu prováděná prvek po prvku. Tato operace totiž není podporována pro všechny typy vektorů, což se může negativně projevit na výkonu některých aplikací, které například provádí váhování, normalizaci atd. Ovšem nejdříve si zopakujme, tak bude vypadat zdrojový kód demonstračního příkladu, který po svém překladu vygeneruje WebAssembly pro celkem deset variant funkcí pro podíl odpovídajících si prvků vektorů:

typedef signed   char           i8x16 __attribute__((vector_size(16)));
typedef unsigned char           u8x16 __attribute__((vector_size(16)));
typedef signed   short int      i16x8 __attribute__((vector_size(16)));
typedef unsigned short int      u16x8 __attribute__((vector_size(16)));
typedef signed   int            i32x4 __attribute__((vector_size(16)));
typedef unsigned int            u32x4 __attribute__((vector_size(16)));
typedef   signed long long int  i64x2 __attribute__((vector_size(16)));
typedef unsigned long long int  u64x2 __attribute__((vector_size(16)));
typedef float                   f32x4 __attribute__((vector_size(16)));
typedef double                  f64x2 __attribute__((vector_size(16)));
 
#define DIV(type) type div_##type(type x, type y) {return (type)(x/y);}
 
#define ALL(type) \
    DIV(type)
 
ALL(i8x16)
ALL(u8x16)
ALL(i16x8)
ALL(u16x8)
ALL(i32x4)
ALL(u32x4)
ALL(i64x2)
ALL(u64x2)
ALL(f32x4)
ALL(f64x2)

11. Překlad operace podílu prvků vektorů s využitím podporovaných instrukcí

Současné verze WebAssembly podporují pouze omezenou možnost výpočtu podílů odpovídajících si prvků vektorů. Velmi dobře je tato operace podporována pro vektory obsahující čtyři prvky typu float:

0003a6 func[8] <div_f32x4>:
 0003a7: 20 00                      | local.get 0
 0003a9: 20 01                      | local.get 1
 0003ab: fd e7 01                   | f32x4.div
 0003ae: 0b                         | end

Totéž platí pro vektory obsahující dva prvky typu double:

0003b0 func[9] <div_f64x2>:
 0003b1: 20 00                      | local.get 0
 0003b3: 20 01                      | local.get 1
 0003b5: fd f3 01                   | f64x2.div
 0003b8: 0b                         | end

Ovšem pro vektory obsahující celočíselné hodnoty je již nutné provést výpočet podílu explicitně prvek po prvku (a tedy dosti neefektivním způsobem). Příkladem může být podíl 32bitových celočíselných prvků bez znaménka:

000330 func[5] <div_u32x4>:
 000331: 20 00                      | local.get 0
 000333: fd 1b 00                   | i32x4.extract_lane 0
 000336: 20 01                      | local.get 1
 000338: fd 1b 00                   | i32x4.extract_lane 0
 00033b: 6e                         | i32.div_u
 00033c: fd 11                      | i32x4.splat
 00033e: 20 00                      | local.get 0
 000340: fd 1b 01                   | i32x4.extract_lane 1
 000343: 20 01                      | local.get 1
 000345: fd 1b 01                   | i32x4.extract_lane 1
 000348: 6e                         | i32.div_u
 000349: fd 1c 01                   | i32x4.replace_lane 1
 00034c: 20 00                      | local.get 0
 00034e: fd 1b 02                   | i32x4.extract_lane 2
 000351: 20 01                      | local.get 1
 000353: fd 1b 02                   | i32x4.extract_lane 2
 000356: 6e                         | i32.div_u
 000357: fd 1c 02                   | i32x4.replace_lane 2
 00035a: 20 00                      | local.get 0
 00035c: fd 1b 03                   | i32x4.extract_lane 3
 00035f: 20 01                      | local.get 1
 000361: fd 1b 03                   | i32x4.extract_lane 3
 000364: 6e                         | i32.div_u
 000365: fd 1c 03                   | i32x4.replace_lane 3
 000368: 0b                         | end

Totéž platí pro vektory obsahující dva 64bitové prvky bez znaménka:

000388 func[7] <div_u64x2>:
 000389: 20 00                      | local.get 0
 00038b: fd 1d 00                   | i64x2.extract_lane 0
 00038e: 20 01                      | local.get 1
 000390: fd 1d 00                   | i64x2.extract_lane 0
 000393: 80                         | i64.div_u
 000394: fd 12                      | i64x2.splat
 000396: 20 00                      | local.get 0
 000398: fd 1d 01                   | i64x2.extract_lane 1
 00039b: 20 01                      | local.get 1
 00039d: fd 1d 01                   | i64x2.extract_lane 1
 0003a0: 80                         | i64.div_u
 0003a1: fd 1e 01                   | i64x2.replace_lane 1
 0003a4: 0b                         | end
Poznámka: podobně se podíl prvků vektorů počítá i v dalších případech (jen je počet instrukcí delší).

12. Tabulka s instrukcemi pro podíl odpovídajících si prvků vektorů

V bajtkódu funkcí přeložených do WebAssembly, který jsme si ukázali v předchozí kapitole, byly přítomny pouze dvě nové SIMD instrukce, a to konkrétně instrukce f32×4.div a f64×2.div. Tyto instrukce jsou pro úplnost uvedeny v následující tabulce:

Operační kód Jméno instrukce Struktura vektorů, které se dělí
fd e7 01 f32×4.div 4 prvky typu single (4×32 bitů)
fd f3 01 f64×2.div 2 prvky typu double (2×64 bitů)

13. Instrukce pro konstrukci nového vektoru

V předchozích demonstračních příkladech jsme se setkali s instrukcí nazvanou splat. Jedná se o instrukci, která z jediné skalární hodnoty vytvoří celý vektor. Typ vektoru je určen prefixem instrukce a od typu vektoru se pochopitelně odvozuje i počet jeho prvků. Těchto instrukcí existuje celkem šest. Není totiž zapotřebí rozlišovat mezi typy se znaménkem a typy bez znaménka:

Operační kód Jméno instrukce Struktura vektorů, které se konstruují
fd 0f i8×16.splat 16 prvků typu byte (16×8 bitů)
fd 10 i16×8.splat 8 prvků typu word (8×16 bitů)
fd 11 i32×4.splat 4 prvky typu double word (4×32 bitů)
fd 12 i64×2.splat 2 prvky typu quad word (2×64 bitů)
fd 13 f32×4.splat 4 prvky typu single (4×32 bitů)
fd 14 f64×2.splat 2 prvky typu double (2×64 bitů)

Kromě toho existují i instrukce typu load, které jsou nepatrně komplikovanější a seznámíme se s nimi příště.

14. Instrukce pro extrakci prvku i pro zpětný zápis prvku do vektoru

Další sada osmi instrukcí slouží k přečtení hodnoty jediného prvku z vektoru. Index čteného prvku je uložen ve třetím bajtu tvořícím operační kód instrukce:

Operační kód Jméno instrukce Struktura vektorů
fd 15 i8×16.extract_lane_s 16 prvků typu byte (16×8 bitů)
fd 16 i8×16.extract_lane_u 16 prvků typu byte (16×8 bitů)
fd 18 i16×8.extract_lane_s 8 prvků typu word (8×16 bitů)
fd 19 i16×8.extract_lane_u 8 prvků typu word (8×16 bitů)
fd 1b i32×4.extract_lane 4 prvky typu double word (4×32 bitů)
fd 1d i64×2.extract_lane 2 prvky typu quad word (2×64 bitů)
fd 1f f32×4.extract_lane 4 prvky typu single (4×32 bitů)
fd 21 f64×2.extract_lane 2 prvky typu double (2×64 bitů)

Ve WebAssembly existují i instrukce s opačným významem, konkrétně takové instrukce, které ve vektoru změní jediný prvek. Skalární hodnota, která má být do prvku uložena, musí být umístěna na zásobníku, index modifikovaného prvku je opět uložen ve třetím bajtu tvořícím operační kód instrukce:

Operační kód Jméno instrukce Struktura vektorů
fd 17 i8×16.replace_lane 16 prvků typu byte (16×8 bitů)
fd 1a i16×8.replace_lane 8 prvků typu word (8×16 bitů)
fd 1c i32×4.replace_lane 4 prvky typu double word (4×32 bitů)
fd 1e i64×2.replace_lane 2 prvky typu quad word (2×64 bitů)
fd 20 f32×4.replace_lane 4 prvky typu single (4×32 bitů)
fd 22 f64×2.replace_lane 2 prvky typu double (2×64 bitů)

15. Překlad složitějších vektorových operací: výpočet skalárního součinu dvou vektorů

S rozvojem neuronových sítí a umělé inteligence (ale nejenom zde) se začal masivně využívat známý algoritmus pro výpočet skalárního součinu (dot product, scalar product). Tento algoritmus se používá v oblasti velkých jazykových modelů (LLM) pro zjišťování podobnosti dlouhých vektorů s numerickými hodnotami (vector similarity). Kromě klasického skalárního součinu se v této oblasti používá i tzv. cosinus similarity, což je varianta skalárního součinu, v níž nezáleží na délce vektorů, ale pouze na jejich vzájemné orientaci (výpočet je tedy doplněn o normalizaci vektorů). A toto porovnávání vektorů se v LLM provádí neustále a většinou je optimalizováno a výpočty běží na GPU.

To však není zdaleka vše. Pokud se zaměříme na oblast klasických neuronových sítí (NN – neural networks), zjistíme, že se tyto sítě skládají z takzvaných perceptronů, což je vlastně značně zjednodušený model neuronu. A na vstup perceptronu se přivádí nějaké množství numerických vstupů. Každý z těchto vstupů je váhován, tj. vynásoben určitou konstantou a výsledky tohoto váhování jsou nakonec sečteny. Když se ovšem nad touto operací zamyslíme, zjistíme, že se vlastně nejedná o nic jiného, než o aplikaci výpočtu skalárního součinu. První z vektorů, který do tohoto součinu vstupuje jako operand, jsou vstupy do neuronu, druhým vektorem je pak vektor vah, které si neuron zapamatoval. A samotný trénink neuronové sítě vlastně není nic jiného, než rekonfigurace těchto vah – vektorů:

Vyzkoušejme si, jakým způsobem se přeloží operace skalárního součinu dvou vektorů, které obsahují buď čtyři prvky typu float nebo osm prvků typu double. Realizace těchto operací v jazyku C je jednoduchá:

typedef float   f32x4 __attribute__((vector_size(16)));
typedef double  f64x2 __attribute__((vector_size(16)));
 
float dot_float(f32x4 x, f32x4 y) {
    return x[0]*y[0]+x[1]*y[1]+x[2]*y[2]+x[3]*y[3];
}
 
double dot_double(f64x2 x, f64x2 y) {
    return x[0]*y[0]+x[1]*y[1];
}

Výsledek překladu první z těchto funkcí do bajtkódu ukazuje, že se pro výpočet nepoužila žádná specializovaná instrukce určená pro výpočet skalárního součinu, ale provedl se explicitní součin odpovídajících si prvků, za nimiž následuje součet všech čtyř mezivýsledků:

00004a func[0] <dot_float>:
 00004b: 20 00                      | local.get 0
 00004d: fd 1f 03                   | f32x4.extract_lane 3
 000050: 20 01                      | local.get 1
 000052: fd 1f 03                   | f32x4.extract_lane 3
 000055: 94                         | f32.mul
 000056: 20 00                      | local.get 0
 000058: fd 1f 02                   | f32x4.extract_lane 2
 00005b: 20 01                      | local.get 1
 00005d: fd 1f 02                   | f32x4.extract_lane 2
 000060: 94                         | f32.mul
 000061: 20 00                      | local.get 0
 000063: fd 1f 00                   | f32x4.extract_lane 0
 000066: 20 01                      | local.get 1
 000068: fd 1f 00                   | f32x4.extract_lane 0
 00006b: 94                         | f32.mul
 00006c: 20 00                      | local.get 0
 00006e: 20 01                      | local.get 1
 000070: fd e6 01                   | f32x4.mul
 000073: fd 1f 01                   | f32x4.extract_lane 1
 000076: 92                         | f32.add
 000077: 92                         | f32.add
 000078: 92                         | f32.add
 000079: 0b                         | end

Totéž platí i pro druhou funkci. Zde se (logicky) provádí jen polovina výpočtů, protože vektory, které skalárně násobíme, mají poloviční počet prvků:

00007b func[1] <dot_double>:
 00007c: 20 00                      | local.get 0
 00007e: fd 21 00                   | f64x2.extract_lane 0
 000081: 20 01                      | local.get 1
 000083: fd 21 00                   | f64x2.extract_lane 0
 000086: a2                         | f64.mul
 000087: 20 00                      | local.get 0
 000089: 20 01                      | local.get 1
 00008b: fd f2 01                   | f64x2.mul
 00008e: fd 21 01                   | f64x2.extract_lane 1
 000091: a0                         | f64.add
 000092: 0b                         | end
Poznámka: u této důležité operace tedy nejsou SIMD instrukce použity, což by se však mělo změnit v nové verzi WebAssembly.

16. Výpočet druhé odmocniny prvků vektorů typu floatdouble

Další často používanou operací, například při zpracování signálů nebo v počítačové grafice, je výpočet druhé odmocniny všech prvků vektoru. Opět si ukážeme, jakým způsobem je tato operace přeložena do WebAssembly v případě, že vektory budou obsahovat prvky typu float nebo double (pro celočíselné prvky se druhá odmocnina počítá méně často a neexistují pro ni ani odpovídající instrukce WebAssembly):

float sqrtf(float x);
double sqrt(double x);
 
typedef float   f32x4 __attribute__((vector_size(16)));
typedef double  f64x2 __attribute__((vector_size(16)));
 
f32x4 vector_sqrt_f32x4(f32x4 x) {
    f32x4 result;
    int i;
    for (i=0; i<4; i++) {
        result[i] = sqrtf(x[i]);
    }
    return result;
}
 
f64x2 vector_sqrt_f64x2(f64x2 x) {
    f64x2 result;
    int i;
    for (i=0; i<2; i++) {
        result[i] = sqrt(x[i]);
    }
    return result;
}
Poznámka: ve funkcích sice používám počítanou programovou smyčku, ovšem moderní překladače céčka ji dokážou transformovat do vektorizovaného kódu.

Výsledek překladu první funkce do bajtkódu WebAssembly ukazuje, že se skutečně použila specializovaná instrukce pro výpočet druhé odmocniny prvků vektorů typu f32×4:

000043 func[0] <vector_sqrt_f32x4>:
 000044: 20 00                      | local.get 0
 000046: fd e3 01                   | f32x4.sqrt
 000049: 0b                         | end

V případě druhé funkce se použila opět instrukce sqrt, ovšem tentokrát s prefixem f64×2:

00004b func[1] <vector_sqrt_f64x2>:
 00004c: 20 00                      | local.get 0
 00004e: fd ef 01                   | f64x2.sqrt
 000051: 0b                         | end

Zjistili jsme tedy, že v bajtkódu WebAssembly jsou podporovány další dvě vektorové instrukce:

Operační kód Jméno instrukce Struktura vektorů, pro které se počítá druhá odmocnina
fd e3 01 f32×4.sqrt 4 prvky typu single (4×32 bitů)
fd ef 01 f64×2.sqrt 2 prvky typu double (2×64 bitů)

17. Instrukce sloužící pro porovnání odpovídajících si prvků vektorů

Poslední sadou instrukcí WebAssembly, s níž se dnes seznámíme, jsou instrukce, které porovnají odpovídající si prvky dvou vektorů na zvolenou relaci. Výsledkem těchto operací přitom bude nový vektor obsahující pouze jedničky a nuly. V případě, že prvky zvolené relaci (například „menší než“) odpovídají, bude ve výsledném vektoru zapsána jednička, v opačném případě nula. Ukažme si jednoduchý příklad:

v1:     [1, 2, 3, 4]
v2:     [1, 1, 4, 4]
v1==v2: [1, 0, 0, 1]
v1>v2:  [0, 1, 0, 0]

Existuje celkem šest testů na relaci: rovnost, nerovnost, menší než, menší nebo rovno, větší než a větší nebo rovno. Teoreticky by mělo být možné porovnat prvky vektorů libovolných typů, přičemž celé porovnání by bylo realizováno jedinou instrukcí. Ovšem jak uvidíme po překladu dalšího příkladu, některé instrukce v současné verzi WebAssembly chybí a tudíž bude porovnání prvků vektorů provedeno pomalu – prvek po prvku.

V dnešním posledním demonstračním příkladu si ověříme, které porovnání vektorů bude realizováno jedinou instrukcí a které nikoli (tedy bude se muset provést porovnání prvek po prvku):

typedef signed   char           i8x16 __attribute__((vector_size(16)));
typedef unsigned char           u8x16 __attribute__((vector_size(16)));
typedef signed   short int      i16x8 __attribute__((vector_size(16)));
typedef unsigned short int      u16x8 __attribute__((vector_size(16)));
typedef signed   int            i32x4 __attribute__((vector_size(16)));
typedef unsigned int            u32x4 __attribute__((vector_size(16)));
typedef   signed long long int  i64x2 __attribute__((vector_size(16)));
typedef unsigned long long int  u64x2 __attribute__((vector_size(16)));
typedef float                   f32x4 __attribute__((vector_size(16)));
typedef double                  f64x2 __attribute__((vector_size(16)));
 
#define EQ(type) type eq_##type(type x, type y) {return x==y;}
#define LT(type) type lt_##type(type x, type y) {return x<y;}
#define LE(type) type le_##type(type x, type y) {return x<=y;}
#define GT(type) type gt_##type(type x, type y) {return x>y;}
#define GE(type) type ge_##type(type x, type y) {return x>=y;}
#define NE(type) type ne_##type(type x, type y) {return x!=y;}
 
#define ALL(type) \
    EQ(type) \
    LT(type) \
    LE(type) \
    GT(type) \
    GE(type) \
    NE(type)
 
ALL(i8x16)
ALL(u8x16)
ALL(i16x8)
ALL(u16x8)
ALL(i32x4)
ALL(u32x4)
ALL(i64x2)
ALL(u64x2)
ALL(f32x4)
ALL(f64x2)

Porovnání, které lze realizovat jedinou instrukcí:

00007e func[0] <eq_i8x16>:
 00007f: 20 00                      | local.get 0
 000081: 20 01                      | local.get 1
 000083: fd 23                      | i8x16.eq
 000085: 0b                         | end
 
000087 func[1] <lt_i8x16>:
 000088: 20 00                      | local.get 0
 00008a: 20 01                      | local.get 1
 00008c: fd 25                      | i8x16.lt_s
 00008e: 0b                         | end
 
000090 func[2] <le_i8x16>:
 000091: 20 00                      | local.get 0
 000093: 20 01                      | local.get 1
 000095: fd 29                      | i8x16.le_s
 000097: 0b                         | end
 
000099 func[3] <gt_i8x16>:
 00009a: 20 00                      | local.get 0
 00009c: 20 01                      | local.get 1
 00009e: fd 27                      | i8x16.gt_s
 0000a0: 0b                         | end
 
0000a2 func[4] <ge_i8x16>:
 0000a3: 20 00                      | local.get 0
 0000a5: 20 01                      | local.get 1
 0000a7: fd 2b                      | i8x16.ge_s
 0000a9: 0b                         | end
 
0000ab func[5] <ne_i8x16>:
 0000ac: 20 00                      | local.get 0
 0000ae: 20 01                      | local.get 1
 0000b0: fd 24                      | i8x16.ne
 0000b2: 0b                         | end
 
0000b4 func[6] <eq_u8x16>:
 0000b5: 20 00                      | local.get 0
 0000b7: 20 01                      | local.get 1
 0000b9: fd 23                      | i8x16.eq
 0000bb: 0b                         | end
 
0000bd func[7] <lt_u8x16>:
 0000be: 20 00                      | local.get 0
 0000c0: 20 01                      | local.get 1
 0000c2: fd 26                      | i8x16.lt_u
 0000c4: 0b                         | end
 
0000c6 func[8] <le_u8x16>:
 0000c7: 20 00                      | local.get 0
 0000c9: 20 01                      | local.get 1
 0000cb: fd 2a                      | i8x16.le_u
 0000cd: 0b                         | end
 
0000cf func[9] <gt_u8x16>:
 0000d0: 20 00                      | local.get 0
 0000d2: 20 01                      | local.get 1
 0000d4: fd 28                      | i8x16.gt_u
 0000d6: 0b                         | end
 
0000d8 func[10] <ge_u8x16>:
 0000d9: 20 00                      | local.get 0
 0000db: 20 01                      | local.get 1
 0000dd: fd 2c                      | i8x16.ge_u
 0000df: 0b                         | end
 
0000e1 func[11] <ne_u8x16>:
 0000e2: 20 00                      | local.get 0
 0000e4: 20 01                      | local.get 1
 0000e6: fd 24                      | i8x16.ne
 0000e8: 0b                         | end
 
0000ea func[12] <eq_i16x8>:
 0000eb: 20 00                      | local.get 0
 0000ed: 20 01                      | local.get 1
 0000ef: fd 2d                      | i16x8.eq
 0000f1: 0b                         | end
 
0000f3 func[13] <lt_i16x8>:
 0000f4: 20 00                      | local.get 0
 0000f6: 20 01                      | local.get 1
 0000f8: fd 2f                      | i16x8.lt_s
 0000fa: 0b                         | end
 
0000fc func[14] <le_i16x8>:
 0000fd: 20 00                      | local.get 0
 0000ff: 20 01                      | local.get 1
 000101: fd 33                      | i16x8.le_s
 000103: 0b                         | end
 
000105 func[15] <gt_i16x8>:
 000106: 20 00                      | local.get 0
 000108: 20 01                      | local.get 1
 00010a: fd 31                      | i16x8.gt_s
 00010c: 0b                         | end
 
00010e func[16] <ge_i16x8>:
 00010f: 20 00                      | local.get 0
 000111: 20 01                      | local.get 1
 000113: fd 35                      | i16x8.ge_s
 000115: 0b                         | end
 
000117 func[17] <ne_i16x8>:
 000118: 20 00                      | local.get 0
 00011a: 20 01                      | local.get 1
 00011c: fd 2e                      | i16x8.ne
 00011e: 0b                         | end
 
000120 func[18] <eq_u16x8>:
 000121: 20 00                      | local.get 0
 000123: 20 01                      | local.get 1
 000125: fd 2d                      | i16x8.eq
 000127: 0b                         | end
 
000129 func[19] <lt_u16x8>:
 00012a: 20 00                      | local.get 0
 00012c: 20 01                      | local.get 1
 00012e: fd 30                      | i16x8.lt_u
 000130: 0b                         | end
 
000132 func[20] <le_u16x8>:
 000133: 20 00                      | local.get 0
 000135: 20 01                      | local.get 1
 000137: fd 34                      | i16x8.le_u
 000139: 0b                         | end
 
00013b func[21] <gt_u16x8>:
 00013c: 20 00                      | local.get 0
 00013e: 20 01                      | local.get 1
 000140: fd 32                      | i16x8.gt_u
 000142: 0b                         | end
 
000144 func[22] <ge_u16x8>:
 000145: 20 00                      | local.get 0
 000147: 20 01                      | local.get 1
 000149: fd 36                      | i16x8.ge_u
 00014b: 0b                         | end
 
00014d func[23] <ne_u16x8>:
 00014e: 20 00                      | local.get 0
 000150: 20 01                      | local.get 1
 000152: fd 2e                      | i16x8.ne
 000154: 0b                         | end
 
000156 func[24] <eq_i32x4>:
 000157: 20 00                      | local.get 0
 000159: 20 01                      | local.get 1
 00015b: fd 37                      | i32x4.eq
 00015d: 0b                         | end
 
00015f func[25] <lt_i32x4>:
 000160: 20 00                      | local.get 0
 000162: 20 01                      | local.get 1
 000164: fd 39                      | i32x4.lt_s
 000166: 0b                         | end
 
000168 func[26] <le_i32x4>:
 000169: 20 00                      | local.get 0
 00016b: 20 01                      | local.get 1
 00016d: fd 3d                      | i32x4.le_s
 00016f: 0b                         | end
 
000171 func[27] <gt_i32x4>:
 000172: 20 00                      | local.get 0
 000174: 20 01                      | local.get 1
 000176: fd 3b                      | i32x4.gt_s
 000178: 0b                         | end
 
00017a func[28] <ge_i32x4>:
 00017b: 20 00                      | local.get 0
 00017d: 20 01                      | local.get 1
 00017f: fd 3f                      | i32x4.ge_s
 000181: 0b                         | end
 
000183 func[29] <ne_i32x4>:
 000184: 20 00                      | local.get 0
 000186: 20 01                      | local.get 1
 000188: fd 38                      | i32x4.ne
 00018a: 0b                         | end
 
00018c func[30] <eq_u32x4>:
 00018d: 20 00                      | local.get 0
 00018f: 20 01                      | local.get 1
 000191: fd 37                      | i32x4.eq
 000193: 0b                         | end
 
000195 func[31] <lt_u32x4>:
 000196: 20 00                      | local.get 0
 000198: 20 01                      | local.get 1
 00019a: fd 3a                      | i32x4.lt_u
 00019c: 0b                         | end
 
00019e func[32] <le_u32x4>:
 00019f: 20 00                      | local.get 0
 0001a1: 20 01                      | local.get 1
 0001a3: fd 3e                      | i32x4.le_u
 0001a5: 0b                         | end
 
0001a7 func[33] <gt_u32x4>:
 0001a8: 20 00                      | local.get 0
 0001aa: 20 01                      | local.get 1
 0001ac: fd 3c                      | i32x4.gt_u
 0001ae: 0b                         | end
 
0001b0 func[34] <ge_u32x4>:
 0001b1: 20 00                      | local.get 0
 0001b3: 20 01                      | local.get 1
 0001b5: fd 40                      | i32x4.ge_u
 0001b7: 0b                         | end
 
0001b9 func[35] <ne_u32x4>:
 0001ba: 20 00                      | local.get 0
 0001bc: 20 01                      | local.get 1
 0001be: fd 38                      | i32x4.ne
 0001c0: 0b                         | end
 
0001c2 func[36] <eq_i64x2>:
 0001c3: 20 00                      | local.get 0
 0001c5: 20 01                      | local.get 1
 0001c7: fd d6 01                   | i64x2.eq
 0001ca: 0b                         | end
 
0001cc func[37] <lt_i64x2>:
 0001cd: 20 00                      | local.get 0
 0001cf: 20 01                      | local.get 1
 0001d1: fd d8 01                   | i64x2.lt_s
 0001d4: 0b                         | end
 
0001d6 func[38] <le_i64x2>:
 0001d7: 20 00                      | local.get 0
 0001d9: 20 01                      | local.get 1
 0001db: fd da 01                   | i64x2.le_s
 0001de: 0b                         | end
 
0001e0 func[39] <gt_i64x2>:
 0001e1: 20 00                      | local.get 0
 0001e3: 20 01                      | local.get 1
 0001e5: fd d9 01                   | i64x2.gt_s
 0001e8: 0b                         | end
 
0001ea func[40] <ge_i64x2>:
 0001eb: 20 00                      | local.get 0
 0001ed: 20 01                      | local.get 1
 0001ef: fd db 01                   | i64x2.ge_s
 0001f2: 0b                         | end
 
0001f4 func[41] <ne_i64x2>:
 0001f5: 20 00                      | local.get 0
 0001f7: 20 01                      | local.get 1
 0001f9: fd d7 01                   | i64x2.ne
 0001fc: 0b                         | end
 
0001fe func[42] <eq_u64x2>:
 0001ff: 20 00                      | local.get 0
 000201: 20 01                      | local.get 1
 000203: fd d6 01                   | i64x2.eq
 000206: 0b                         | end
 
0002a8 func[47] <ne_u64x2>:
 0002a9: 20 00                      | local.get 0
 0002ab: 20 01                      | local.get 1
 0002ad: fd d7 01                   | i64x2.ne
 0002b0: 0b                         | end
 
0002b2 func[48] <eq_f32x4>:
 0002b3: 20 00                      | local.get 0
 0002b5: 20 01                      | local.get 1
 0002b7: fd 41                      | f32x4.eq
 0002b9: 0b                         | end
 
0002bb func[49] <lt_f32x4>:
 0002bc: 20 00                      | local.get 0
 0002be: 20 01                      | local.get 1
 0002c0: fd 43                      | f32x4.lt
 0002c2: 0b                         | end
 
0002c4 func[50] <le_f32x4>:
 0002c5: 20 00                      | local.get 0
 0002c7: 20 01                      | local.get 1
 0002c9: fd 45                      | f32x4.le
 0002cb: 0b                         | end
 
0002cd func[51] <gt_f32x4>:
 0002ce: 20 00                      | local.get 0
 0002d0: 20 01                      | local.get 1
 0002d2: fd 44                      | f32x4.gt
 0002d4: 0b                         | end
 
0002d6 func[52] <ge_f32x4>:
 0002d7: 20 00                      | local.get 0
 0002d9: 20 01                      | local.get 1
 0002db: fd 46                      | f32x4.ge
 0002dd: 0b                         | end
 
0002df func[53] <ne_f32x4>:
 0002e0: 20 00                      | local.get 0
 0002e2: 20 01                      | local.get 1
 0002e4: fd 42                      | f32x4.ne
 0002e6: 0b                         | end
 
0002e8 func[54] <eq_f64x2>:
 0002e9: 20 00                      | local.get 0
 0002eb: 20 01                      | local.get 1
 0002ed: fd 47                      | f64x2.eq
 0002ef: 0b                         | end
 
0002f1 func[55] <lt_f64x2>:
 0002f2: 20 00                      | local.get 0
 0002f4: 20 01                      | local.get 1
 0002f6: fd 49                      | f64x2.lt
 0002f8: 0b                         | end
 
0002fa func[56] <le_f64x2>:
 0002fb: 20 00                      | local.get 0
 0002fd: 20 01                      | local.get 1
 0002ff: fd 4b                      | f64x2.le
 000301: 0b                         | end
 
000303 func[57] <gt_f64x2>:
 000304: 20 00                      | local.get 0
 000306: 20 01                      | local.get 1
 000308: fd 4a                      | f64x2.gt
 00030a: 0b                         | end
 
00030c func[58] <ge_f64x2>:
 00030d: 20 00                      | local.get 0
 00030f: 20 01                      | local.get 1
 000311: fd 4c                      | f64x2.ge
 000313: 0b                         | end
 
000315 func[59] <ne_f64x2>:
 000316: 20 00                      | local.get 0
 000318: 20 01                      | local.get 1
 00031a: fd 48                      | f64x2.ne
 00031c: 0b                         | end

Kupodivu však nejsou podporovány některé instrukce pro porovnání vektorů s prvky typu unsigned long long. Ovšem tyto vektory jsou velmi krátké (dva prvky), takže výsledný kód ve WebAssembly není příliš dlouhý:

000208 func[43] <lt_u64x2>:
 000209: 42 7f                      | i64.const 18446744073709551615
 00020b: 42 00                      | i64.const 0
 00020d: 20 00                      | local.get 0
 00020f: fd 1d 00                   | i64x2.extract_lane 0
 000212: 20 01                      | local.get 1
 000214: fd 1d 00                   | i64x2.extract_lane 0
 000217: 54                         | i64.lt_u
 000218: 1b                         | select
 000219: fd 12                      | i64x2.splat
 00021b: 42 7f                      | i64.const 18446744073709551615
 00021d: 42 00                      | i64.const 0
 00021f: 20 00                      | local.get 0
 000221: fd 1d 01                   | i64x2.extract_lane 1
 000224: 20 01                      | local.get 1
 000226: fd 1d 01                   | i64x2.extract_lane 1
 000229: 54                         | i64.lt_u
 00022a: 1b                         | select
 00022b: fd 1e 01                   | i64x2.replace_lane 1
 00022e: 0b                         | end
 
000230 func[44] <le_u64x2>:
 000231: 42 7f                      | i64.const 18446744073709551615
 000233: 42 00                      | i64.const 0
 000235: 20 00                      | local.get 0
 000237: fd 1d 00                   | i64x2.extract_lane 0
 00023a: 20 01                      | local.get 1
 00023c: fd 1d 00                   | i64x2.extract_lane 0
 00023f: 58                         | i64.le_u
 000240: 1b                         | select
 000241: fd 12                      | i64x2.splat
 000243: 42 7f                      | i64.const 18446744073709551615
 000245: 42 00                      | i64.const 0
 000247: 20 00                      | local.get 0
 000249: fd 1d 01                   | i64x2.extract_lane 1
 00024c: 20 01                      | local.get 1
 00024e: fd 1d 01                   | i64x2.extract_lane 1
 000251: 58                         | i64.le_u
 000252: 1b                         | select
 000253: fd 1e 01                   | i64x2.replace_lane 1
 000256: 0b                         | end
 
000258 func[45] <gt_u64x2>:
 000259: 42 7f                      | i64.const 18446744073709551615
 00025b: 42 00                      | i64.const 0
 00025d: 20 00                      | local.get 0
 00025f: fd 1d 00                   | i64x2.extract_lane 0
 000262: 20 01                      | local.get 1
 000264: fd 1d 00                   | i64x2.extract_lane 0
 000267: 56                         | i64.gt_u
 000268: 1b                         | select
 000269: fd 12                      | i64x2.splat
 00026b: 42 7f                      | i64.const 18446744073709551615
 00026d: 42 00                      | i64.const 0
 00026f: 20 00                      | local.get 0
 000271: fd 1d 01                   | i64x2.extract_lane 1
 000274: 20 01                      | local.get 1
 000276: fd 1d 01                   | i64x2.extract_lane 1
 000279: 56                         | i64.gt_u
 00027a: 1b                         | select
 00027b: fd 1e 01                   | i64x2.replace_lane 1
 00027e: 0b                         | end
 
000280 func[46] <ge_u64x2>:
 000281: 42 7f                      | i64.const 18446744073709551615
 000283: 42 00                      | i64.const 0
 000285: 20 00                      | local.get 0
 000287: fd 1d 00                   | i64x2.extract_lane 0
 00028a: 20 01                      | local.get 1
 00028c: fd 1d 00                   | i64x2.extract_lane 0
 00028f: 5a                         | i64.ge_u
 000290: 1b                         | select
 000291: fd 12                      | i64x2.splat
 000293: 42 7f                      | i64.const 18446744073709551615
 000295: 42 00                      | i64.const 0
 000297: 20 00                      | local.get 0
 000299: fd 1d 01                   | i64x2.extract_lane 1
 00029c: 20 01                      | local.get 1
 00029e: fd 1d 01                   | i64x2.extract_lane 1
 0002a1: 5a                         | i64.ge_u
 0002a2: 1b                         | select
 0002a3: fd 1e 01                   | i64x2.replace_lane 1
 0002a6: 0b                         | end

Shrňme si výsledky, které jsme právě získali, do tabulky:

Školení Zabbix

Operační kód Jméno instrukce Prováděná operace Struktura vektorů, které se porovnávají
fd 23 i8×16.eq test na rovnost prvků 16 prvků typu byte (16×8 bitů)
fd 24 i8×16.ne test na nerovnost prvků 16 prvků typu byte (16×8 bitů)
fd 25 i8×16.lt_s relace „menší než“ 16 prvků typu signed char (16×8 bitů)
fd 29 i8×16.le_s relace „menší nebo rovno“ 16 prvků typu signed char (16×8 bitů)
fd 27 i8×16.gt_s relace „větší než“ 16 prvků typu signed char (16×8 bitů)
fd 2b i8×16.ge_s relace „větší nebo rovno“ 16 prvků typu signed char (16×8 bitů)
fd 26 i8×16.lt_u relace „menší než“ 16 prvků typu unsigned char (16×8 bitů)
fd 2a i8×16.le_u relace „menší nebo rovno“ 16 prvků typu unsigned char (16×8 bitů)
fd 28 i8×16.gt_u relace „větší než“ 16 prvků typu unsigned char (16×8 bitů)
fd 2c i8×16.ge_u relace „větší nebo rovno“ 16 prvků typu unsigned char (16×8 bitů)
       
fd 2d i16×8.eq test na rovnost prvků 8 prvků typu word (8×16 bitů)
fd 2e i16×8.ne test na nerovnost prvků 8 prvků typu word (8×16 bitů)
fd 2f i16×8.lt_s relace „menší než“ 8 prvků typu signed word (8×16 bitů)
fd 33 i16×8.le_s relace „menší nebo rovno“ 8 prvků typu signed word (8×16 bitů)
fd 31 i16×8.gt_s relace „větší než“ 8 prvků typu signed word (8×16 bitů)
fd 35 i16×8.ge_s relace „větší nebo rovno“ 8 prvků typu signed word (8×16 bitů)
fd 30 i16×8.lt_u relace „menší než“ 8 prvků typu usigned word (8×16 bitů)
fd 34 i16×8.le_u relace „menší nebo rovno“ 8 prvků typu usigned word (8×16 bitů)
fd 32 i16×8.gt_u relace „větší než“ 8 prvků typu usigned word (8×16 bitů)
fd 36 i16×8.ge_u relace „větší nebo rovno“ 8 prvků typu usigned word (8×16 bitů)
       
fd 37 i32×4.eq test na rovnost prvků 4 prvky typu long (4×32 bitů)
fd 38 i32×4.ne test na nerovnost prvků 4 prvky typu long (4×32 bitů)
fd 39 i32×4.lt_s relace „menší než“ 4 prvky typu signed long (4×32 bitů)
fd 3d i32×4.le_s relace „menší nebo rovno“ 4 prvky typu signed long (4×32 bitů)
fd 3b i32×4.gt_s relace „větší než“ 4 prvky typu signed long (4×32 bitů)
fd 3f i32×4.ge_s relace „větší nebo rovno“ 4 prvky typu signed long (4×32 bitů)
fd 3a i32×4.lt_u relace „menší než“ 4 prvky typu usigned long (4×32 bitů)
fd 3e i32×4.le_u relace „menší nebo rovno“ 4 prvky typu usigned long (4×32 bitů)
fd 3c i32×4.gt_u relace „větší než“ 4 prvky typu usigned long (4×32 bitů)
fd 40 i32×4.ge_u relace „větší nebo rovno“ 4 prvky typu usigned long (4×32 bitů)
       
fd d6 01 i64×2.eq test na rovnost prvků 2 prvky typu long long (2×64 bitů)
fd d7 01 i64×2.ne test na nerovnost prvků 2 prvky typu long long (2×64 bitů)
fd d8 01 i64×2.lt_s relace „menší než“ 2 prvky typu long signed long (2×64 bitů)
fd da 01 i64×2.le_s relace „menší nebo rovno“ 2 prvky typu long signed long (2×64 bitů)
fd d9 01 i64×2.gt_s relace „větší než“ 2 prvky typu long signed long (2×64 bitů)
fd db 01 i64×2.ge_s relace „větší nebo rovno“ 2 prvky typu long signed long (2×64 bitů)
       
fd 41 f32×4.eq test na rovnost prvků 4 prvky typu float (4×32 bitů)
fd 43 f32×4.lt test na nerovnost prvků 4 prvky typu float (4×32 bitů)
fd 45 f32×4.le relace „menší než“ 4 prvky typu float (4×32 bitů)
fd 44 f32×4.gt relace „menší nebo rovno“ 4 prvky typu float (4×32 bitů)
fd 46 f32×4.ge relace „větší než“ 4 prvky typu float (4×32 bitů)
fd 42 f32×4.ne relace „větší nebo rovno“ 4 prvky typu float (4×32 bitů)
       
fd 47 f64×2.eq test na rovnost prvků 2 prvky typu double (2×64 bitů)
fd 49 f64×2.lt test na nerovnost prvků 2 prvky typu double (2×64 bitů)
fd 4b f64×2.le relace „menší než“ 2 prvky typu double (2×64 bitů)
fd 4a f64×2.gt relace „menší nebo rovno“ 2 prvky typu double (2×64 bitů)
fd 4c f64×2.ge relace „větší než“ 2 prvky typu double (2×64 bitů)
fd 48 f64×2.ne relace „větší nebo rovno“ 2 prvky typu double (2×64 bitů)

18. Tabulka se všemi doposud popsanými instrukcemi

Všechny instrukce WebAssembly, které jsme si až doposud popsali, jsou vypsány v následující tabulce, kde jsou seřazeny podle svého operačního kódu (tedy na základě hodnoty svého prvního bajtu). Aby bylo zřejmé, jaký rozsah instrukčního souboru již byl popsán, obsahuje tabulka i (prozatím) prázdné řádky:

Operační kód Jméno instrukce Stručný popis
0×00    
0×01    
0×02 block uloží na zásobník řízení toku (control-flow stack)
0×03 loop návěští je nastavena na současnou pozici v bajtkódu a zapamatováno
0×04    
0×05    
0×06    
0×07    
0×08    
0×09    
0×0a    
0×0b end konec bloku nebo konec celé funkce
0×0c br provede se nepodmíněný skok
0×0d br_if pokud je na zásobníku uložena nenulová hodnota, provede se skok, jinak se neprovede žádná operace
0×0e br_table rozeskok mezi více bloky
0×0f return ukončení funkce s předáním návratových hodnot přes zásobník
0×10 call zavolání funkce
0×11    
0×12    
0×13    
0×14    
0×15    
0×16    
0×17    
0×18    
0×19    
0×1a drop odstranění hodnoty nebo hodnot ze zásobníku operandů
0×1b select ze zásobníku přečte tři hodnoty, na základě výsledku podmínky vrátí na zásobník druhou nebo třetí hodnotu
0×1c    
0×1d    
0×1e    
0×1f    
0×20 local.get uložení hodnoty lokální proměnné na zásobník
0×21 local.set přenos hodnoty z vrcholu zásobníku do lokální proměnné
0×22 local.tee kopie hodnoty z vrcholu zásobníku do lokální proměnné
0×23    
0×24    
0×25    
0×26    
0×27    
0×28    
0×29    
0×2a    
0×2b    
0×2c    
0×2d    
0×2e    
0×2f    
0×30    
0×31    
0×32    
0×33    
0×34    
0×35    
0×36    
0×37    
0×38    
0×39    
0×3a    
0×3b    
0×3c    
0×3d    
0×3e    
0×3f    
0×41 i32.const uložení 32bitové celočíselné konstanty na zásobník
0×42 i64.const uložení 64bitové celočíselné konstanty na zásobník
0×43 f32.const uložení 32bitové konstanty s plovoucí řádovou čárkou na zásobník
0×44 f64.const uložení 64bitové konstanty s plovoucí řádovou čárkou na zásobník
0×44    
0×45    
0×46 i32.eq porovnání operandů typu int32 na relaci „rovno“
0×47 i32.ne porovnání operandů typu int32 na relaci „nerovno“
0×48 i32.lt_s porovnání operandů typu int32 na relaci „menší než“ (se znaménkem)
0×49 i32.lt_u porovnání operandů typu int32 na relaci „menší než“ (bez znaménka)
0×4a i32.gt_s porovnání operandů typu int32 na relaci „větší než“ (se znaménkem)
0×4b i32.gt_u porovnání operandů typu int32 na relaci „větší než“ (bez znaménka)
0×4c i32.le_s porovnání operandů typu int32 na relaci „menší nebo rovno“ (se znaménkem)
0×4d i32.le_u porovnání operandů typu int32 na relaci „menší nebo rovno“ (bez znaménka)
0×4e i32.ge_s porovnání operandů typu int32 na relaci „větší nebo rovno“ (se znaménkem)
0×4f i32.ge_u porovnání operandů typu int32 na relaci „větší nebo rovno“ (bez znaménka)
0×50    
0×51 i64.eq porovnání operandů typu int64 na relaci „rovno“
0×52 i64.ne porovnání operandů typu int64 na relaci „nerovno“
0×53 i64.lt_s porovnání operandů typu int64 na relaci „menší než“ (se znaménkem)
0×54 i64.lt_u porovnání operandů typu int64 na relaci „menší než“ (bez znaménka)
0×55 i64.gt_s porovnání operandů typu int64 na relaci „větší než“ (se znaménkem)
0×56 i64.gt_u porovnání operandů typu int64 na relaci „větší než“ (bez znaménka)
0×57 i64.le_s porovnání operandů typu int64 na relaci „menší nebo rovno“ (se znaménkem)
0×58 i64.le_u porovnání operandů typu int64 na relaci „menší nebo rovno“ (bez znaménka)
0×59 i64.ge_s porovnání operandů typu int64 na relaci „větší nebo rovno“ (se znaménkem)
0×5a i64.ge_u porovnání operandů typu int64 na relaci „větší nebo rovno“ (bez znaménka)
0×5b f32.eq porovnání dvou hodnot typu single na relaci „rovno“
0×5c f32.ne porovnání dvou hodnot typu single na relaci „nerovno“
0×5d f32.lt porovnání dvou hodnot typu single na relaci „menší než“
0×5e f32.gt porovnání dvou hodnot typu single na relaci „větší než“
0×5f f32.le porovnání dvou hodnot typu single na relaci „menší nebo rovno“
0×60 f32.ge porovnání dvou hodnot typu single na relaci „větší nebo rovno“
0×61 f64.eq porovnání dvou hodnot typu double na relaci „rovno“
0×62 f64.ne porovnání dvou hodnot typu double na relaci „nerovno“
0×63 f64.lt porovnání dvou hodnot typu double na relaci „menší než“
0×64 f64.gt porovnání dvou hodnot typu double na relaci „větší než“
0×65 f64.le porovnání dvou hodnot typu double na relaci „menší nebo rovno“
0×66 f64.ge porovnání dvou hodnot typu double na relaci „větší nebo rovno“
0×67    
0×68    
0×69    
0×6a i32.add součet dvou celých 32bitových hodnot
0×6b i32.sub rozdíl dvou celých 32bitových hodnot
0×6c i32.mul součin dvou celých 32bitových hodnot
0×6d i32.div_s podíl dvou celých 32bitových hodnot se znaménkem
0×6e i32.div_u podíl dvou celých 32bitových hodnot bez znaménka
0×6f i32.rem_s zbytek po dělení dvou celých 32bitových hodnot se znaménkem
0×70 i32.rem_u zbytek po dělení dvou celých 32bitových hodnot bez znaménka
0×71    
0×72    
0×73    
0×74    
0×75    
0×76    
0×77    
0×78    
0×79    
0×7a    
0×7b    
0×7c i64.add součet dvou celých 64bitových hodnot
0×7d i64.sub rozdíl dvou celých 64bitových hodnot
0×7e i64.mul součin dvou celých 64bitových hodnot
0×7f i64.div_s podíl dvou celých 64bitových hodnot se znaménkem
0×80 i64.div_u podíl dvou celých 64bitových hodnot bez znaménka
0×81 i64.rem_s zbytek po dělení dvou celých 64bitových hodnot se znaménkem
0×82 i64.rem_u zbytek po dělení dvou celých 64bitových hodnot bez znaménka
0×83    
0×84    
0×85    
0×86    
0×87    
0×88    
0×89    
0×8a    
0×8b f32.abs absolutní hodnota typu single/float
0×8c f32.neg otočení znaménka u hodnoty typu single/float
0×8d    
0×8e    
0×8f    
0×90    
0×91 f32.sqrt druhá odmocnina z hodnoty typu single/float
0×92 f32.add součet dvou hodnot typu single/float
0×93 f32.sub rozdíl dvou hodnot typu single/float
0×94 f32.mul součin dvou hodnot typu single/float
0×95 f32.div podíl dvou hodnot typu single/float
0×96 f32.min součet dvou hodnot typu single/float
0×97 f32.max součet dvou hodnot typu single/float
0×98    
0×99 f64.abs absolutní hodnota typu double
0×9a f64.neg otočení znaménka u hodnoty typu double
0×9b    
0×9c    
0×9d    
0×9e    
0×9f f64.sqrt druhá odmocnina z hodnoty typu double
0×a0 f64.add součet dvou hodnot typu double
0×a1 f64.sub rozdíl dvou hodnot typu double
0×a2 f64.mul součin dvou hodnot typu double
0×a3 f64.div podíl dvou hodnot typu double
0×a4 f64.min součet dvou hodnot typu double
0×a5 f64.max součet dvou hodnot typu double
0×a6    
0×a7 i32.wrap_i64 de facto opak instrukce extend, převod hodnoty se ztrátou informace 64 na 32 bitů
0×a8 i32.trunc_f32_s převod hodnoty typu float na celé číslo se znaménkem
0×a9 i32.trunc_f32_u převod hodnoty typu float na celé číslo bez znaménka
0×aa i32.trunc_f64_s převod hodnoty typu double na celé číslo se znaménkem
0×ab i32.trunc_f64_u převod hodnoty typu double na celé číslo bez znaménka
0×ac i64.extend_i32_s znaménkové rozšíření hodnoty (32 na 64 bitů)
0×ad i64.extend_i32_u bezznaménkové rozšíření hodnoty (32 na 64 bitů)
0×ae i64.trunc_f32_s převod hodnoty typu float na celé číslo se znaménkem
0×af i64.trunc_f32_u převod hodnoty typu float na celé číslo bez znaménka
0×b0 i64.trunc_f64_s převod hodnoty typu double na celé číslo se znaménkem
0×b1 i64.trunc_f64_u převod hodnoty typu double na celé číslo bez znaménka
0×b2 f32.convert_i32_s konverze celého čísla se znaménkem (32 bitů) na typ float
0×b3 f32.convert_i32_u konverze celého čísla bez znaménka (32 bitů) na typ float
0×b4 f32.convert_i64_s konverze celého čísla se znaménkem (32 bitů) na typ float
0×b5 f32.convert_i64_u konverze celého čísla bez znaménka (32 bitů) na typ float
0×b6 f32.demote_f64 převod hodnoty typu double na typ float
0×b7 f64.convert_i32_s konverze celého čísla se znaménkem (64 bitů) na typ double
0×b8 f64.convert_i32_u konverze celého čísla bez znaménka (64 bitů) na typ double
0×b9 f64.convert_i64_s konverze celého čísla se znaménkem (64 bitů) na typ double
0×ba f64.convert_i64_u konverze celého čísla bez znaménka (64 bitů) na typ double
0×bb f64.promote_f32 převod hodnoty typu float na typ double
0×bc i32.reinterpret_f32 pouze změna typu, nezmění se však jednotlivé bity slova
0×bd i64.reinterpret_f64 pouze změna typu, nezmění se však jednotlivé bity slova
0×be f32.reinterpret_i32 pouze změna typu, nezmění se však jednotlivé bity slova
0×bf f64.reinterpret_i64 pouze změna typu, nezmění se však jednotlivé bity slova
0×c0 i32.extend8_s znaménkové rozšíření hodnoty z 8 bitů na 32 bitů
0×c1 i32.extend16_s znaménkové rozšíření hodnoty ze 16 bitů na 32 bitů
0×c2 i64.extend8_s znaménkové rozšíření hodnoty z 8 bitů na 64 bitů
0×c3 i64.extend16_s znaménkové rozšíření hodnoty ze 16 bitů na 64 bitů
0×c4 i64.extend32_s znaménkové rozšíření hodnoty z 8 bitů na 64 bitů
0×c5    
0×c6    
0×c7    
0×c8    
0×c9    
0×ca    
0×cb    
0×cc    
0×cd    
0×ce    
0×cf    
0×d0    
0×d1    
0×d2    
0×d3    
0×d4    
0×d5    
0×d6    
0×d7    
0×d8    
0×d9    
0×da    
0×db    
0×dc    
0×dd    
0×de    
0×df    
0×e0    
0×e1    
0×e2    
0×e3    
0×e4    
0×e5    
0×e6    
0×e7    
0×e8    
0×e9    
0×ea    
0×eb    
0×ec    
0×ed    
0×ee    
0×ef    
0×f0    
0×f1    
0×f2    
0×f3    
0×f4    
0×f5    
0×f6    
0×f7    
0×f8    
0×f9    
0×fa    
0×fb    
0×fc    
fd 0f i8×16.splat skalár převedený na 16 prvků typu byte (16×8 bitů)
fd 10 i16×8.splat skalár převedený na 8 prvků typu word (8×16 bitů)
fd 11 i32×4.splat skalár převedený na 4 prvky typu double word (4×32 bitů)
fd 12 i64×2.splat skalár převedený na 2 prvky typu quad word (2×64 bitů)
fd 13 f32×4.splat skalár převedený na 4 prvky typu single (4×32 bitů)
fd 14 f64×2.splat skalár převedený na 2 prvky typu double (2×64 bitů)
fd 15 i8×16.extract_lane_s 16 prvků typu byte (16×8 bitů)
fd 16 i8×16.extract_lane_u 16 prvků typu byte (16×8 bitů)
fd 17 i8×16.replace_lane 16 prvků typu byte (16×8 bitů)
fd 18 i16×8.extract_lane_s 8 prvků typu word (8×16 bitů)
fd 19 i16×8.extract_lane_u 8 prvků typu word (8×16 bitů)
fd 1a i16×8.replace_lane 8 prvků typu word (8×16 bitů)
fd 1b i32×4.extract_lane 4 prvky typu double word (4×32 bitů)
fd 1c i32×4.replace_lane 4 prvky typu double word (4×32 bitů)
fd 1d i64×2.extract_lane 2 prvky typu quad word (2×64 bitů)
fd 1e i64×2.replace_lane 2 prvky typu quad word (2×64 bitů)
fd 1f f32×4.extract_lane 4 prvky typu single (4×32 bitů)
fd 20 f32×4.replace_lane 4 prvky typu single (4×32 bitů)
fd 21 f64×2.extract_lane 2 prvky typu double (2×64 bitů)
fd 22 f64×2.replace_lane 2 prvky typu double (2×64 bitů)
fd 23 i8×16.eq test na rovnost prvků; 16 prvků typu byte (16×8 bitů)
fd 24 i8×16.ne test na nerovnost prvků; 16 prvků typu byte (16×8 bitů)
fd 25 i8×16.lt_s relace „menší než“; 16 prvků typu signed char (16×8 bitů)
fd 26 i8×16.lt_u relace „menší než“; 16 prvků typu unsigned char (16×8 bitů)
fd 27 i8×16.gt_s relace „větší než“; 16 prvků typu signed char (16×8 bitů)
fd 28 i8×16.gt_u relace „větší než“; 16 prvků typu unsigned char (16×8 bitů)
fd 29 i8×16.le_s relace „menší nebo rovno“; 16 prvků typu signed char (16×8 bitů)
fd 2a i8×16.le_u relace „menší nebo rovno“; 16 prvků typu unsigned char (16×8 bitů)
fd 2b i8×16.ge_s relace „větší nebo rovno“; 16 prvků typu signed char (16×8 bitů)
fd 2c i8×16.ge_u relace „větší nebo rovno“; 16 prvků typu unsigned char (16×8 bitů)
fd 2d i16×8.eq test na rovnost prvků; 8 prvků typu word (8×16 bitů)
fd 2e i16×8.ne test na nerovnost prvků; 8 prvků typu word (8×16 bitů)
fd 2f i16×8.lt_s relace „menší než“; 8 prvků typu signed word (8×16 bitů)
fd 30 i16×8.lt_u relace „menší než“; 8 prvků typu usigned word (8×16 bitů)
fd 31 i16×8.gt_s relace „větší než“; 8 prvků typu signed word (8×16 bitů)
fd 32 i16×8.gt_u relace „větší než“; 8 prvků typu usigned word (8×16 bitů)
fd 33 i16×8.le_s relace „menší nebo rovno“; 8 prvků typu signed word (8×16 bitů)
fd 34 i16×8.le_u relace „menší nebo rovno“; 8 prvků typu usigned word (8×16 bitů)
fd 35 i16×8.ge_s relace „větší nebo rovno“; 8 prvků typu signed word (8×16 bitů)
fd 36 i16×8.ge_u relace „větší nebo rovno“; 8 prvků typu usigned word (8×16 bitů)
fd 37 i32×4.eq test na rovnost prvků; 4 prvky typu long (4×32 bitů)
fd 38 i32×4.ne test na nerovnost prvků; 4 prvky typu long (4×32 bitů)
fd 39 i32×4.lt_s relace „menší než“; 4 prvky typu signed long (4×32 bitů)
fd 3a i32×4.lt_u relace „menší než“; 4 prvky typu usigned long (4×32 bitů)
fd 3b i32×4.gt_s relace „větší než“; 4 prvky typu signed long (4×32 bitů)
fd 3c i32×4.gt_u relace „větší než“; 4 prvky typu usigned long (4×32 bitů)
fd 3d i32×4.le_s relace „menší nebo rovno“; 4 prvky typu signed long (4×32 bitů)
fd 3e i32×4.le_u relace „menší nebo rovno“; 4 prvky typu usigned long (4×32 bitů)
fd 3f i32×4.ge_s relace „větší nebo rovno“; 4 prvky typu signed long (4×32 bitů)
fd 40 i32×4.ge_u relace „větší nebo rovno“; 4 prvky typu usigned long (4×32 bitů)
fd 41 f32×4.eq test na rovnost prvků; 4 prvky typu float (4×32 bitů)
fd 42 f32×4.ne relace „větší nebo rovno“; 4 prvky typu float (4×32 bitů)
fd 43 f32×4.lt test na nerovnost prvků; 4 prvky typu float (4×32 bitů)
fd 44 f32×4.gt relace „menší nebo rovno“; 4 prvky typu float (4×32 bitů)
fd 45 f32×4.le relace „menší než“; 4 prvky typu float (4×32 bitů)
fd 46 f32×4.ge relace „větší než“; 4 prvky typu float (4×32 bitů)
fd 47 f64×2.eq test na rovnost prvků; 2 prvky typu double (2×64 bitů)
fd 48 f64×2.ne relace „větší nebo rovno“; 2 prvky typu double (2×64 bitů)
fd 49 f64×2.lt test na nerovnost prvků; 2 prvky typu double (2×64 bitů)
fd 4a f64×2.gt relace „menší nebo rovno“; 2 prvky typu double (2×64 bitů)
fd 4b f64×2.le relace „menší než“; 2 prvky typu double (2×64 bitů)
fd 4c f64×2.ge relace „větší než“; 2 prvky typu double (2×64 bitů)
fd 61 i8×16.neg otočení znaménka vektorů se šestnácti prvky typu byte (16×8 bitů)
fd 6e i8×16.add součet vektorů se šestnácti prvky typu byte (16×8 bitů)
fd 71 i8×16.sub rozdíl vektorů se šestnácti prvky typu byte (16×8 bitů)
fd 81 01 i16×8.neg otočení znaménka vektorů s osmi typu word (8×16 bitů)
fd 8e 01 i16×8.add součet vektorů s osmi prvky typu word (8×16 bitů)
fd 91 01 i16×8.sub rozdíl vektorů s osmi prvky typu word (8×16 bitů)
fd 95 01 i16×8.mul součin odpovídajících si prvků vektorů typu word (8×16 bitů)
fd a1 01 i32×4.neg otočení znaménka vektorů se čtyřmi prvky typu double word (4×32 bitů)
fd ae 01 i32×4.add součet vektorů se čtyřmi prvky typu double word (4×32 bitů)
fd b1 01 i32×4.sub rozdíl vektorů se čtyřmi prvky typu double word (4×32 bitů)
fd b5 01 i32×4.mul součin odpovídajících si prvků vektorů typu double word (4×32 bitů)
fd c1 01 i64×2.neg otočení znaménka vektorů se dvěma prvky typu quat word (2×64 bitů)
fd ce 01 i64×2.add součet vektorů se dvěma prvky typu quad word (2×64 bitů)
fd d1 01 i64×2.sub rozdíl vektorů se dvěma prvky typu quat word (2×64 bitů)
fd d5 01 i64×2.mul součin odpovídajících si prvků vektorů typu quad word (2×64 bitů)
fd d6 01 i64×2.eq test na rovnost prvků; 2 prvky typu long long (2×64 bitů)
fd d7 01 i64×2.ne test na nerovnost prvků; 2 prvky typu long long (2×64 bitů)
fd d8 01 i64×2.lt_s relace „menší než“; 2 prvky typu long signed long (2×64 bitů)
fd d9 01 i64×2.gt_s relace „větší než“; 2 prvky typu long signed long (2×64 bitů)
fd da 01 i64×2.le_s relace „menší nebo rovno“; 2 prvky typu long signed long (2×64 bitů)
fd db 01 i64×2.ge_s relace „větší nebo rovno“; 2 prvky typu long signed long (2×64 bitů)
fd e1 01 f32×4.neg otočení znaménka vektorů se čtyřmi prvky tyypu single (4×32 bitů)
fd e3 01 f32×4.sqrt výpočet druhé odmocniny čtyř prvků typu single (4×32 bitů)
fd e6 01 f32×4.mul součin odpovídajících si prvků vektorů typu single (4×32 bitů)
fd e7 01 f32×4.div podíl odpovídajících si prvků vektorů typu single (4×32 bitů)
fd e4 01 f32×4.add součet vektorů se čtyřmi prvky typu single (4×32 bitů)
fd e5 01 f32×4.sub rozdíl vektorů se čtyřmi prvky typu single (4×32 bitů)
fd ed 01 f64×2.neg otočení znaménka vektorů se dvěma prvky typu double (2×64 bitů)
fd ef 01 f64×2.sqrt výpočet druhé odmocniny dvou prvků typu double (2×64 bitů)
fd f0 01 f64×2.add součet vektorů se dvěma prvky typu double (2×64 bitů)
fd f1 01 f64×2.sub rozdíl vektorů se dvěma prvky typu double (2×64 bitů)
fd f2 01 f64×2.mul součin odpovídajících prvků vektorů double (2×64 bitů)
fd f3 01 f64×2.div podíl odpovídajících prvků vektorů double (2×64 bitů)
0×fe    
0×ff    

19. Články o SIMD, které doposud na Rootu vyšly

Podporou SIMD instrukcí na úrovni takzvaných intrinsic v jazyku C jsme se už na Rootu zabývali, stejně jako samotnými SIMD instrukcemi na úrovni assembleru (i když jen pro platformu x86 či x86–64 a nikoli pro WebAssembly). Pro úplnost jsou v této příloze uvedeny odkazy na příslušné články:

  1. Užitečné rozšíření GCC: podpora SIMD (vektorových) instrukcí
    https://www.root.cz/clanky/uzitecne-rozsireni-gcc-podpora-simd-vektorovych-instrukci/
  2. Užitečné rozšíření GCC – podpora SIMD (vektorových) instrukcí: nedostatky technologie
    https://www.root.cz/clanky/uzitecne-rozsireni-gcc-podpora-simd-vektorovych-instrukci-nedostatky-technologie/
  3. Podpora SIMD (vektorových) instrukcí na RISCových procesorech
    https://www.root.cz/clanky/podpora-simd-vektorovych-instrukci-na-riscovych-procesorech/
  4. Podpora SIMD operací v GCC s využitím intrinsic pro nízkoúrovňové optimalizace
    https://www.root.cz/clanky/podpora-simd-operaci-v-gcc-s-vyuzitim-intrinsic-pro-nizkourovnove-optimalizace/
  5. Podpora SIMD operací v GCC s využitím intrinsic: technologie SSE
    https://www.root.cz/clanky/podpora-simd-operaci-v-gcc-s-vyuzitim-intrinsic-technologie-sse/
  6. Rozšíření instrukční sady „Advanced Vector Extensions“ na platformě x86–64
    https://www.root.cz/clanky/rozsireni-instrukcni-sady-advanced-vector-extensions-na-platforme-x86–64/
  7. Rozšíření instrukční sady F16C, FMA a AVX-512 na platformě x86–64
    https://www.root.cz/clanky/rozsireni-instrukcni-sady-f16c-fma-a-avx-512-na-platforme-x86–64/
  8. Rozšíření instrukční sady AVX-512 na platformě x86–64 (dokončení)
    https://www.root.cz/clanky/rozsireni-instrukcni-sady-avx-512-na-platforme-x86–64-dokonceni/
  9. SIMD instrukce na platformě 80×86: instrukční sada MMX
    https://www.root.cz/clanky/simd-instrukce-na-platforme-80×86-instrukcni-sada-mmx/
  10. SIMD instrukce na 80×86: dokončení popisu MMX, instrukce 3DNow!
    https://www.root.cz/clanky/simd-instrukce-na-80–86-dokonceni-popisu-mmx-instrukce-3dnow/
  11. SIMD instrukce v rozšíření SSE
    https://www.root.cz/clanky/simd-instrukce-v-rozsireni-sse/
  12. SIMD instrukce v rozšíření SSE (2. část)
    https://www.root.cz/clanky/simd-instrukce-v-rozsireni-sse-2-cast/
  13. Pokročilejší SSE operace: přeskupení, promíchání a rozbalování prvků vektorů
    https://www.root.cz/clanky/po­krocilejsi-sse-operace-preskupeni-promichani-a-rozbalovani-prvku-vektoru/
  14. Od instrukční sady SSE k sadě SSE2
    https://www.root.cz/clanky/od-instrukcni-sady-sse-k-sade-sse2/
  15. Instrukční sady SIMD a automatické vektorizace prováděné překladačem GCC
    https://www.root.cz/clanky/instrukcni-sady-simd-a-automaticke-vektorizace-provadene-prekladacem-gcc/
  16. Instrukční sady SIMD a automatické vektorizace prováděné překladačem GCC (2)
    https://www.root.cz/clanky/instrukcni-sady-simd-a-automaticke-vektorizace-provadene-prekladacem-gcc-2/
  17. Pohled pod kapotu formátu WebAssembly: SIMD (vektorové) operace
    https://www.root.cz/clanky/pohled-pod-kapotu-formatu-webassembly-simd-vektorove-operace/

20. Odkazy na Internetu

  1. Compiling C to WebAssembly without Emscripten
    https://surma.dev/things/c-to-webassembly/
  2. Web Assemply: Text Format
    https://webassembly.github­.io/spec/core/text/index.html
  3. WebAssembly: Binary Format
    https://webassembly.github­.io/spec/core/binary/index­.html
  4. WebAssembly
    https://webassembly.org/
  5. WebAssembly na Wiki Golangu
    https://github.com/golang/go/wi­ki/WebAssembly
  6. The future of WebAssembly – A look at upcoming features and proposals
    https://blog.scottlogic.com/2018/07/20/wasm-future.html
  7. WebAssembly Design
    https://github.com/WebAssembly/design
  8. Využití WebAssembly z programovacího jazyka Go
    https://www.root.cz/clanky/vyuziti-webassembly-z-programovaciho-jazyka-go/
  9. WebAssembly slibuje podstatné zrychlení webů, konec JavaScriptu se ale nekoná
    https://www.lupa.cz/clanky/webassembly-slibuje-podstatne-zrychleni-webu-konec-javascriptu-se-ale-nekona/
  10. List of languages that compile to JS
    https://github.com/jashke­nas/coffeescript/wiki/List-of-languages-that-compile-to-JS
  11. asm.js
    http://asmjs.org/
  12. Top 23 WASM Open-Source Projects
    https://www.libhunt.com/topic/wasm
  13. Made with WebAssembly
    https://madewithwebassembly.com/
  14. The Top 1,790 Wasm Open Source Projects on Github
    https://awesomeopensource­.com/projects/wasm
  15. Sanspiel
    https://sandspiel.club/
  16. Painting on HTML5 Canvas with Rust WASM
    https://www.subarctic.org/pa­inting_on_html5_canvas_wit­h_rust_wasm.html
  17. Writing WebAssembly By Hand
    https://blog.scottlogic.com/2018/04/26/we­bassembly-by-hand.html
  18. WebAssembly Specification
    https://webassembly.github­.io/spec/core/index.html
  19. Index of Instructions
    https://webassembly.github­.io/spec/core/appendix/in­dex-instructions.html
  20. The WebAssembly Binary Toolkit
    https://github.com/WebAssembly/wabt
  21. Will WebAssembly replace JavaScript? Or Will WASM Make JavaScript More Valuable in Future?
    https://dev.to/vaibhavshah/will-webassembly-replace-javascript-or-will-wasm-make-javascript-more-valuable-in-future-5c6e
  22. Webassembly as 32bit and 64bit
    https://stackoverflow.com/qu­estions/78580226/webassem­bly-as-32bit-and-64bit
  23. Portability
    https://webassembly.org/doc­s/portability/
  24. Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
    https://www.root.cz/clanky/he­xadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/
  25. Nástroj objdump: švýcarský nožík pro vývojáře
    https://www.root.cz/clanky/nastroj-objdump-svycarsky-nozik-pro-vyvojare/
  26. Getting Started: Building and Running Clang
    https://clang.llvm.org/get_star­ted.html
  27. Clang: a C language family frontend for LLVM
    https://clang.llvm.org/
  28. Scheduling LLVM Passes with the New Pass Manager
    https://stephenverderame.git­hub.io/blog/scheduling_llvm/
  29. C data types
    https://en.wikipedia.org/wi­ki/C_data_types
  30. WebAssembly data types
    https://webassembly.github­.io/spec/core/syntax/types­.html
  31. WebAssembly Opcodes
    https://pengowray.github.io/wasm-ops/
  32. Advanced tools (for WebAssembly)
    https://webassembly.org/getting-started/advanced-tools/
  33. Binaryen
    https://github.com/WebAssem­bly/binaryen
  34. Using SIMD with WebAssembly
    https://emscripten.org/doc­s/porting/simd.html
  35. GCC documentation: Extensions to the C Language Family
    https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html#C-Extensions
  36. GCC documentation: Using Vector Instructions through Built-in Functions
    https://gcc.gnu.org/online­docs/gcc/Vector-Extensions.html
  37. SSE (Streaming SIMD Extentions)
    http://www.songho.ca/misc/sse/sse­.html
  38. Timothy A. Chagnon: SSE and SSE2
    http://www.cs.drexel.edu/~tc365/mpi-wht/sse.pdf
  39. Intel corporation: Extending the Worldr's Most Popular Processor Architecture
    http://download.intel.com/techno­logy/architecture/new-instructions-paper.pdf
  40. SIMD architectures:
    http://arstechnica.com/ol­d/content/2000/03/simd.ar­s/
  41. 3Dnow! Technology Manual
    AMD Inc., 2000
  42. Intel MMXTM Technology Overview
    Intel corporation, 1996
  43. MultiMedia eXtensions
    http://softpixel.com/~cwrig­ht/programming/simd/mmx.phpi
  44. AMD K5 („K5“ / „5k86“)
    http://www.pcguide.com/ref/cpu/fam/g5K5-c.html
  45. Arm Helium
    https://www.arm.com/techno­logies/helium
  46. SIMD proposal for WebAssembly
    https://github.com/webassembly/simd/
  47. Single instruction, multiple data
    https://en.wikipedia.org/wi­ki/Single_instruction%2C_mul­tiple_data
  48. Parallel computing
    https://en.wikipedia.org/wi­ki/Parallel_computing
  49. Flynn's taxonomy
    https://en.wikipedia.org/wi­ki/Flynn%27s_taxonomy
  50. WebAssembly opcode table
    https://pengowray.github.io/wasm-ops/

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.