SIMD

Table of Contents

从Pentium II和Pentium MMX处理器系列开始,芯片开始支持SIMD操作. SIMD全称是Single Instruction Multiple Data,即单指令多数据, 可以在一个周期内操作多个数据.

1 指令集

1.1 MMX

  • Multimedia Extensions.[PII & PMMX]
  • (CPUID.01H:EDX.MMX[bit 23]=1)

1.2 SSE.

  • Streaming SIMD Extensions.[PIII]
  • (CPUID.01H:EDX.SSE[bit 25]=1)

1.3 SSE2

  • Streaming SIMD Extensions2.[P4 & Xeon]
  • (CPUID.01H:EDX.SSE2[bit 26]=1)

1.4 SSE3

  • Streaming SIMD Extensions3.[P4(HT)]
  • (CPUID.01H:ECX.SSE3[bit 0]=1)

1.5 SSSE3

  • Supplemental Stream SIMD Extensions3.[Xeon(5100) & Core2]
  • (CPUID.01H:ECX.SSSE3[bit 9]=1)

1.6 SSE4

  • Streaming SIMD Extensions4.[Xeon(5400) & Core2Ex(QX9650)]
  • SSE4.1.(CPUID.01H:ECX.SSE4_1[bit 19]=1)
  • SSE4.2.(CPUID.01H:ECX.SSE4_2[bit 20]=1)

1.7 检测代码

/***************************************************************************
 *
 * Copyright (c) Baidu.com, Inc. All Rights Reserved
 *
 **************************************************************************/


/**
 * @file   detect.c
 * @author zhangyan04(@baidu.com)
 * @brief
 *
 */

#include <stdio.h>
int check_support_mmx(){
    int res=0;
    __asm__ __volatile__(
        "movl $1,%%eax\n\t"
        "cpuid\n\t"
        "test $0x800000,%%edx\n\t"
        "jz 1f\n\t"
        "movl $1,%0\n\t"
        "1:\n\t"
        :"=m"(res)
        ::"eax","edx");
    return res;
}
int check_support_sse(){
    int res=0;
    __asm__ __volatile__(
        "movl $1,%%eax\n\t"
        "cpuid\n\t"
        "test $0x02000000,%%edx\n\t"
        "jz 1f\n\t"
        "movl $1,%0\n\t"
        "1:\n\t"
        :"=m"(res)
        ::"eax","edx");
    return res;
}
int check_support_sse2(){
    int res=0;
    __asm__ __volatile__(
        "movl $1,%%eax\n\t"
        "cpuid\n\t"
        "test $0x04000000,%%edx\n\t"
        "jz 1f\n\t"
        "movl $1,%0\n\t"
        "1:\n\t"
        :"=m"(res)
        ::"eax","edx");
    return res;
}
int check_support_sse3(){
    int res=0;
    __asm__ __volatile__(
        "movl $1,%%eax\n\t"
        "cpuid\n\t"
        "test $0x1,%%ecx\n\t"
        "jz 1f\n\t"
        "movl $1,%0\n\t"
        "1:\n\t"
        :"=m"(res)
        ::"eax","edx");
    return res;
}
int check_support_ssse3(){
    int res=0;
    __asm__ __volatile__(
        "movl $1,%%eax\n\t"
        "cpuid\n\t"
        "test $0x0200,%%ecx\n\t"
        "jz 1f\n\t"
        "movl $1,%0\n\t"
        "1:\n\t"
        :"=m"(res)
        ::"eax","edx");
    return res;
}
int check_support_sse4_1(){
    int res=0;
    __asm__ __volatile__(
        "movl $1,%%eax\n\t"
        "cpuid\n\t"
        "test $0x80000,%%ecx\n\t"
        "jz 1f\n\t"
        "movl $1,%0\n\t"
        "1:\n\t"
        :"=m"(res)
        ::"eax","edx");
    return res;
}
int check_support_sse4_2(){
    int res=0;
    __asm__ __volatile__(
        "movl $1,%%eax\n\t"
        "cpuid\n\t"
        "test $0x0100000,%%ecx\n\t"
        "jz 1f\n\t"
        "movl $1,%0\n\t"
        "1:\n\t"
        :"=m"(res)
        ::"eax","edx");
    return res;
}
int main(){
    printf("MMX[%s]\n",check_support_mmx()?"OK":"FAILED");
    printf("SSE[%s]\n",check_support_sse()?"OK":"FAILED");
    printf("SSE2[%s]\n",check_support_sse2()?"OK":"FAILED");
    printf("SSE3[%s]\n",check_support_sse3()?"OK":"FAILED");
    printf("SSSE3[%s]\n",check_support_ssse3()?"OK":"FAILED");
    printf("SSE4.1[%s]\n",check_support_sse4_1()?"OK":"FAILED");
    printf("SSE4.2[%s]\n",check_support_sse4_2()?"OK":"FAILED");
    return 0;
}

2 基本概念

2.1 %mm寄存器

%mm寄存器是64bit,共有8个%mm寄存器.需要注意的是,%mm0-%mm7是X87 FPU寄存器的alias, 分别对应%r0-%r7.所以对%mm0-%mm7的操作会覆盖X87 FPU的内容.使用%mm寄存器的时候, 效果是这样的.

  1. TOS(Top Of Stack)会被置为0,也就是FPU registers的顶部会置0.
  2. 整个FPU tag word会被置为valid(0x0).如果后续想使用的话,需要使用EMMS指令.
  3. FPU register有80位,但是%mm寄存器只是用了64位,因此其余位填充(0xff).

因此如果在使用%mm寄存器之后,想使用FPU指令的话,那么应该

  1. fsave/fxsave保存FPU状态.
  2. 执行EMMS指令.
  3. 可选地使用frstore/fxstore载入之前FPU状态.
  4. 执行FPU指令.

如果使用FPU指令之后,想切换回%mm寄存器的话.

  1. fsave/fxsave保存FPU状态.
  2. 可选地使用frstore/fxrstore载入之前FPU状态.
  3. 操作%mm寄存器.

EMMS指令会清除MMX的状态,将FPU tag word进行清空,表示所有的FPU registers都已经清空. 我们必须在执行完成MMX指令之后,如果之后需要使用FPU registers的话,那么需要执行这个指令.

2.2 %xmm寄存器

%xmm寄存器是128bit. Intel64架构下允许访问16个%xmm寄存器. IA-32架构下只允许访问8个%xmm寄存器.

2.3 %mxcsr寄存器

%mxcsr是32bit.%mxcsr寄存器是在SSE指令集引入的,用来控制作用在%xmm寄存器操作的行为, 所有的这些行为都是和浮点相关的,在某种程度上非常类似于X87 FPU tag word. 关于%mxcsr寄存器各个位所表示的意思在这里不细说,可以查看Intel手册得到详细解释. 可以查看Intel Vol.1 10.2.3.%mxcrs默认值是0x1f80.

指令 说明
LDMXCSR mem->%mxcsr.32bit
STMXCSR %mxcsr->mem.32bit

2.4 Saturation & Wraparound

在进行整数运算的时候,可能会存在out-of-range的情况,结果不能够被目标数所表示.对于 这种溢出处理有下面3种方式.

  • Wraparound Arithmetic.

回绕模式.比如8个字节表示257的话,那么就是257-256=1.

  • Signed Saturation Arithmetic.

符号位溢出模式.比如8个字节表示257的话,那么会是0x7f=127.

  • Unsigned Saturation Arithmetic.

无符号溢出模式.比如8个字节表示257的话,那么会是0xff=255.

对于溢出模式对于一些计算是非常重要的.假设256色的像素如果两个像素相叠加的话, 当然不希望像素值发生回绕.如果溢出的话,通常这个像素保持纯黑或者是纯白.

2.5 General Purpose Register(GPR)

通用寄存器,包括EAX/RAX,EBX/RBX,ECX/RCX等.这些通用寄存器和%mm和%xmm之间的差别是, %mm和%xmm不能够用来存放地址,也就是说不能够将内存地址存放在%mm和%xmm里面然后进行引用.

2.6 X87 FPU

X87 FPU是浮点运算部件,共有8个寄存器,组织方式是堆栈.通常来说对于SIMD并不需要关心 X87 FPU这个部件.但是因为SIMD使用的%mm寄存器是FPU寄存器的alias,所以我们这里需要了解. 后面我们把X87 FPU都称为FPU.

对于FPU会有一个状态,状态包括执行环境和寄存器内容.每个寄存器80bit.在操作%mm寄存器 和执行FPU指令切换之间,我们可能需要保存状态.那么下面就是关于FPU操作状态的指令.

指令 说明
FSAVE 保存FPU状态,然后重新初始化FPU.84/108字节
FRSTORE FSAVE逆操作.
FXSAVE 保存FPU状态/%mm寄存器,%xmm寄存器,%mxscr寄存器.512字节.
FXRSTORE FXSAVE逆操作.

关于如何协调%mm寄存器和FPU寄存器的使用,在%mm寄存器这节有解释.

2.7 Packed & Scalar Instructions

对于SIMD提供了操作packed和scalar指令.我们假设存在两个操作数, 假设是(f00,f01,f02,f03)和(f10,f11,f12,f13)的话,那么

  • 如果是packed操作的话,那么操作是(f00 op f01,f01 op f11,f02 op f12,f03 op f13).
  • 如果是scalar操作的话,那么操作是(f00,f01,f03,f03 op f13).

也就是说,如果在scalar操作的话,仅仅是操作最后面一个单元,其他单元全部复制.

需要注意的是,在Scalar操作下面

  • 单精度浮点是24-bit significand + 8-bit exponent.
  • 双精度浮点是53-bit significand + 11-bit exponent.

而在IEEE-754和FPU操作环境下面的的话

  • 单精度浮点是24-bit significand + 15-bit exponent.
  • 双精度浮点是52-bit significand + 15-bit exponent.

此外SIMD操作浮点数和FPU操作浮点数有些不同,SIMD是直接操作浮点数的Native Format, 而FPU是首先在更高的精度上面操作,然后取舍到Native Format.

2.8 Temporal & NonTemporal Data

待续.需要阅读Intel Vol.3A Memory & Cache Control这节.在Intel Vol.1 10.4.6.2也有介绍.

2.9 Alignment

关于对齐方面,如果使用128bit Memory Operand必须进行16字节的对齐.但是有些例外

  • 使用UnAlign的Data Transfer操作,比如MOVUPS/MOVUPD.
  • 如果是Scalar Memory Float的话,必须是4字节对齐.
  • 如果是Scalar Memory Double的话,必须是8字节对齐.
  • 此外还有部分指令字节对齐存在例外,会在响应的指令部分说明.

2.10 Asymmetric & Horizontal Processing

分别是对称处理和水平处理.假设存在操作数(a0,a1,a2,a3)以及(b0,b1,b2,b3). 对于大部分SIMD指令处理都是对称处理,也就是(a0 op b0,a1 op b1,a2 op b2,a3 op b3). 相邻处理就是(a0 op a1,a2 op a3,b0 op b1,b2 op b3).

2.11 Zero Fill & Truncated

对于从内存/寄存器载入到寄存器的话,如果位数不够,通常是占用寄存器的低字节, 除非显式指定.对于寄存器中没有使用的高字节,通常是采用0填充,也就是Zero Fill.:).

而另外一个方面,如果从寄存器传输到内存/寄存器,如果寄存器位数过多的话,那么也 通常只是传输寄存器的低字节,而保留寄存器的高字节,也就是Truncated.:).

3 指令

为了方便表示,我们定义下面缩写和操作.

助记符 含义 其他
A Aligned  
U UnAligned  
L Low  
H High/Horizontal  
B Byte  
SB Signed Byte  
UB Unsigned Byte  
W Word  
SW Signed Word  
UW Unsigned Word  
Q Quad Word  
DQ Double Quad Word  
F Float  
D Double  
PS Packed Single Precision Floating Point  
SS Scalar Single Precision Floating Point  
PD Packed Double Precision Floating Point  
SD Scalar Double Precision Floating Point  
CMP Compare  
STR String  
EQ Equal  
GT Greater  
SLL Shift Left Logical  
SRL Shift Right Logical  
SRA Shift Right Arithmetic  
DUP Duplicate  
WAM Wraparound Mode  
SSM Signed Saturation Mode  
USM Unsigned Saturation Mode  
RCP Reciprocal.RCP(x)=1/x  
SQRT Square Root  
RSQRT Reciprocal Square Root  
MSK Mask  
CVT Convert  
SX Signed Extend  
ZX Zero Extend  
ROUND    
UNPCK Unpack  
EXTR Extract  
INSR Insert  
AND a && b  
OR a or b  
NAND !(a && b)  
XOR a ^ b  
SAD Sum of Absolute Difference.  
SIGN SIGN(src,dst)=if(src<0):dst=-dst  
MADD MADD((a00,a01),(b00,b01))=(a00*b00)+(a01*b01)  
ALIGNR ALIGNR(src,dst,imm)=(src,dst) >> imm  
AVG Average  
ABS Absolute  
NT NonTemporal  
CVTT Convert With Truncate  
UNPCKH UNPCKH((s00,s01),(d00,d01))=(d01,s01)  
UNPCKL UNPCKL((s00,s01),(d00,d01))=(d00,s00)  
MSB Most Significant Bit  
LF Lowest Float  
LF2 Lower 2 Floats  
LF4 Lower 4 Floats  
HF Highest Float  
HF2 Higher 2 Floats  
LD Lowest Double  
HD Highest Double  
LDW Lower Double Word  
LDW2 Lower 2 Double Words  
LDW4 Lower 4 Double Words  
LW Lower Word  
HW Higher Wword  
GPR General Purpose Resgister  

这里有几点需要注意的

  • 对于Move如果使用了错误类型指令的话,会产生性能消耗.Vol.1 11.6.9
  • 对于使用SIMD来说,推荐使用caller-save.Vol.1 11.6.10.3

3.1 Data Transfer Instructions

3.1.1 Move Mask Instructions

对于每一个data element的MSB移到GRP.这些指令通常用于分支判定.

指令 说明
PMOVMSKB  
MOVMSKPS  
MOVMSKPD  

3.1.2 Move Integer Instructions

指令 说明
MOVD  
MOVQ  
MOVDQA  
MOVDQU  
LDDQU 功能和MOVDQU相同

关于LDDQU和MOVDQU的差别,可以参看Intel关于LDDQU指令描述,主要还是在某些场景 下面的性能差别,功能上没有任何区别.

3.1.3 Move Float Instructions

指令 说明
MOVAPS  
MOVUPS  
MOVSS  
MOVLPS 2PS<->LF2(%xmm)
MOVHPS 2PS<->HF2(%xmm)
MOVLHPS LF2(%xmm1)->HF2(%xmm2)
MOVHLPS HF2(%xmm1)->LF2(%xmm1)

3.1.4 Move Double Instructions

指令 说明
MOVAPD  
MOVUPD  
MOVSD  
MOVLPD PD<->LD(%xmm)
MOVHPD PD<->HD(%xmm)

3.1.5 Move Duplication Instructions

指令 说明
MOVDDUP (d0,d1)->(d0,d0)
MOVSHDUP (f0,f1,f2,f3)->(f1,f1,f3,f3)
MOVSLDUP (f0,f1,f2,f3)->(f0,f0,f2,f2)

3.1.6 Move NonTemporal Instructions

指令 说明
MOVNTI  
MOVNTQ  
MOVNTDQ  
MOVNTDQA  
MOVNTPS  
MOVNTPD  
MASKMOVQ @@TODO
MASKMOVDQU @@TODO

3.2 Arithmetic Instructions

3.2.1 ADD Instructions

  1. 对称处理
    指令 说明
    PADDB WAM
    PADDW WAM
    PADDD WAM
    PADDQ WAM
    PADDSB SSM
    PADDSW SSM
    PADDUSB USM
    PADDUSW USM
    ADDPS  
    ADDSS  
    ADDPD  
    ADDSD  
  2. 水平处理
    指令 说明
    PHADDW  
    PHADDSW SSM
    PHADDD  
    HADDPS  
    HADDPD  

3.2.2 SUB Instructions

  1. 对称处理
    指令 说明
    PSUBB WAM
    PSUBW WAM
    PSUBD WAM
    PSUBQ WAM
    PSUBSB SSM
    PSUBSW SSM
    PSUBSD SSM
    PSUBUSB USM
    PSUBUSW USM
    SUBPS  
    SUBSS  
    SUBPD  
    SUBSD  
  2. 水平处理
    指令 说明
    PHSUBW  
    PHSUBSW SSM
    PHSUBD  
    HSUBPS  
    HSUBPD  

3.2.3 MUL Instructions

指令 说明
PMULLW (w00,w01,..),(w10,w11,..)->(LW(w00*w10),LW(w01*w11),..)
PMULHW (w00,w01,..),(w10,w11,..)->(HW(w00*w10),HW(w01*w11),..)
PMULHUW 同上,Unsigned方式.
PMULLD (dw00,dw01,..),(dw10,dw11,..)->(LDW(dw00*dw10),LDW(dw01*dw11),..)
PMULDQ (dw00,dw01,..),(dw10,dw11,..)->(dw00*dw10,dw01*dw11,..)
PMULUDQ 同上,Unsigned方式.
MULPS  
MULSS  
MULPD  
MULSD  

3.2.4 DIV Instructions

指令 说明
DIVPS  
DIVSS  
DIVPD  
DIVSD  

3.2.5 M?X Instructions

  1. MAX
    指令 说明
    PMAXSB  
    PMAXSW  
    PMAXSD  
    PMAXUB  
    PMAXUW  
    PMAXUD  
    MAXPS  
    MAXSS  
    MAXPD  
    MAXSD  
  2. MIN
    指令 说明
    PMINSB  
    PMINSW  
    PMINSD  
    PMINUB  
    PMINUW  
    PMINUD  
    MINPS  
    MINSS  
    MINPD  
    MINSD  
    PHMINPOSUW  

    PHMINPOSUW可以按照Unsigned Word来水平搜索最小值的位置.

3.2.6 Math Instructions

指令 说明
PABSB  
PABSW  
PABSD  
PAVGB  
PAVGW  
PSIGNB  
PSIGNW  
PSIGND  
PMADDUBSW UB->W.SSM
PMADDWD W->DW
PALIGNR  
PMULHRSW @@TODO
PSADBW  
MPSADBW @@TODO
DPPS  
DPPD  
ADDSUBPS (f00,f01,..),(f10,f11,..)->(f00-f10,f01+f11,..)
ADDSUBPD (d00,d01,..),(d10,d11,..)->(d00-d10,d01+d11,..)
RCPPS  
RCPSS  
RSQRTPS  
RSQRTSS  
SQRTPS  
SQRTSS  
SQRTSD  

3.3 Comparison Instructions

需要注意的是,如果没有特殊说明,比较结果是直接存放在结果数里面的, 不会影响EFLAGS这个寄存器内容.如果比较结果影响了EFLAGS寄存器的话, 那么会使用%EFLAGS来标记.

如果每个比较结果是符合预期的话, 那么目的数对应位数会置0xff,否则会置0x0.

对于CMP的指令,会使用立即数来决定具体使用什么比较方式.关于立即数对应 什么比较方式,可以查看具体指令里面的说明,比如CMPPS,CMPSS,CMPPD,CMPSD.

指令 说明
PCMPEQB  
PCMPEQW  
PCMPEQD  
PCMPEQQ  
PCMPGTB  
PCMPGTW  
PCMPGTD  
PCMPGTQ  
CMPPS  
CMPSS  
CMPPD  
CMPSD  
COMISS %EFLAGS
UCOMISS %EFLAGS
COMISD %EFLAGS
UCOMISD %EFLAGS
PTEST  

3.4 Conversion Instructions

对于涉及到浮点数的精度取舍问题,使用%mxccsr寄存器来判断.

如果精度取舍是采用截断方式来进行处理的话,那么指令前缀通常是CVTT.

指令 说明
PACKSSWB SSM
PACKSSDW SSM
PACKUSDW USM
指令 说明
CVTPS2PD  
CVTPD2PS  
CVTSS2SD  
CVTSD2SS  
CVTPI2PS  
CVTPS2PI  
CVTTPS2PI  
CVTSI2SS  
CVTSS2SI  
CVTTSS2SI  
CVTPI2PD  
CVTPD2PI  
CVTTPD2PI  
CVTSI2SD  
CVTSD2SI  
CVTTSD2SI  
CVTDQ2PS  
CVTPS2DQ  
CVTTPS2DQ  
CVTDQ2PD  
CVTPD2DQ  
CVTTPD2DQ  
指令 说明
MOVQ2DQ  
MOVDQ2Q  
PMOVSXBW  
PMOVZXBW  
PMOVSXBD  
PMOVZXBD  
PMOVSXWD  
PMOVZXWD  
PMOVSXBQ  
PMOVZXBQ  
PMOVSXWQ  
PMOVZXWQ  
PMOVSXDQ  
PMOVZXDQ  
指令 说明
ROUNDPS  
ROUNDPD  
ROUNDSS  
ROUNDSD  

3.5 Insert & Unpack Instructions

指令 说明
PUNPCKHBW  
PUNPCKHWD  
PUNPCKHDQ  
PUNPCKHQDQ  
PUNPCKLBW  
PUNPCKLWD  
PUNPCKLDQ  
PUNPCKLQDQ  
UNPCKHPS  
UNPCKLPS  
UNPCKHPD  
UNPCKLPD  
指令 说明
PEXTRB  
PEXTRW  
PEXTRD  
PEXTRQ  
PINSRB  
PINSRW  
PINSRD  
PINSRQ  
EXTRACTPS ->GPR
INSERTPS  

3.6 Logical Instructions

指令 说明
PAND  
PANDN  
POR  
PXOR  
ANDPS  
ANDNPS  
ORPS  
XORPS  
ANDPD  
ANDNPD  
ORPD  
XORPD  

3.7 Shift Instructions

指令 说明
PSLLW  
PSLLD  
PSLLQ  
PSLLDQ  
PSRLW  
PSRLD  
PSRLQ  
PSRLDQ  
PSRAW  
PSRAD  

3.8 Shuffle Instructions

SHUF操作根据imm来决定,dst每个位置的element应该是由 src的哪个位置的element来进行填充的.

指令 说明
PSHUFB  
PSHUFW  
PSHUFLW  
PSHUFHW  
PSHUFD  
SHUFPS  
SHUFPD  

3.9 Blending Instructions

BLEND操作是根据imm来决定,dst每个位置的element应该是从src里面对应位置取出, 还是应该从dst里面对应位置取出.

BLENDV操作和BLEND操作过程一样的,不同的是由%xmm0来决定的而不是由imm来决定.

指令 说明
BLENDPS  
BLENDPD  
BLENDVPS  
BLENDVPD  
PBLENDVB  
PBLENDW  

3.10 String Instructions

  • 内存操作数不要求字节对齐.
  • CMPE/I的E表示explicit显式指定长度,I表示implicit隐式指定长度.
  • STRI/STRM的I表示结果是Index,M表示结果是Mask.
指令 说明
PCMPESTRI  
PCMPESTRM  
PCMPISTRI  
PCMPISTRM  

3.11 MISC Instructions

3.11.1 Cache Control Instructions

  • CLFLUSH

CLFLUSH是cache line flush,能够将某个内存地址的cache line全部失效.

3.11.2 Prefetch Instructions

对于预取指令的话不会影响程序行为,通常来说会预取32个对齐的字节,但是具体 还是依赖于实现.对于NT数据的话,依然会尽可能地减少Cache的污染.

  • PREFETCH0

预取到所有Cache层次.

  • PREFETCH1

预取到1级缓存.

  • PREFETCH2

预取到2级缓存.

  • PREFETCHNTA

???

3.11.3 Memory Ordering Instructions

  • SFENCE

在SFENCE之前的Store操作,从全局视图来看,一定在SFENCE之后的Store操作之前完成.

  • LFENCE

在LFENCE之前的Load操作,从全局视图来看,一定在LFENCE之后的Load操作之前完成.

  • MFENCE

MFENCE结合了SFENCE和LFENCE两个功能.

3.11.4 X87 FPU Instructions

  • FISTTP

这条指令非常类似FISTP,是将FPU TOS的浮点数转换成为整数,精度处理使用截断. FISTP需要修改FPU Tag Word设置为截断处理才会有这样的效果,

3.11.5 Thread Sync Instructions

需要注意的是,这些指令都只能够在ring0级别下面运行,对于<ring0的界别是可选运行的.

  • MONITOR

设置一块地址区域来监视是否存在write-back-stores的操作.

  • MWAIT

等待某块地址区域发生write-back-stores.这块地址区域必须经过MONITOR设置.在等待 这块地址区域写入的时候Logical Processor能够进入optimized state.

3.11.6 其他

  • PAUSE

PAUSE指令能够显著改善自旋锁的循环等待期间的性能,同时减少机器的耗能.

  • Branch Hints

对于Jcc这样的指令,允许在之前加上2EH,3EH作为Prefix能够进行预取提示. 这个没有特别的助记符,只是在生成的机器代码二进制上略有不同.

  • CRC32

CRC32算法的有效实现.

  • POPCNT

计算操作数的bit表示中存在多少个1.