【汇编】SIMD

CPUID

        通过使用CPUID指令、检查控制寄存器位和读取特定型号的寄存器来获取特征标志、状态和系统信息。

表1-1 CPU识别

操作码指令Op/En64位模式兼容/传统模式描述
OF A2CPUIDZ0有效有效将处理器标识和特征信息返回到EAX、EBX、ECX和EDX寄存器,由输入EAX决定(在某些情况下,ECX也可以)

表1-2 指令操作数编码


Op/En操作码1操作码2操作码3操作码4
Z0N/AN/AN/AN/A

表1-3 CPUID指令提供的信息

输入EAX的值处理器提供的信息
0H

EAX CPUID基本信息最大输入值

EBX "Genu"

ECX "ntel"

EDX "inel"

1H

EAX 版本信息:类型,家族,型号,步进ID

EBX 07-00:品牌索引(brand index)

        15-08:CLFLUSH行大小(value*8=缓存行大小,字节为单位,CLFLUSHOPT也使用)

        23-16:物理包中逻辑处理器的最大地址ID

        31-24:初始高级可编程中断控制器(APIC)ID

ECX 特征信息

EDX 特征信息

        详:见官方手册。

SIMD

        AVX指令使用一种编码方案进行编码,该方案将前缀字节、操作码扩展字段、操作数编码字段和向量长度编码能力组合成一个新的前缀,称为VEX。在VEX编码方案中,VEX前缀可能是2或3个字节长,这取决于指令语义。

        SSE和AVX指令包含若干变体:原始SSE、增强SSE2、SSE3、SSE4(SSE4.1和SSE4.2)、AVX、AVX2以及-AVX512。SSE3是与Pentium 4F(Prescoot)CPU同时推出的。

        SSE和AVX体系结构指令主要有如下三代:

  • SSE体系结构,提供16个128位的XMM寄存器,支持整数和浮点数据类型

  • AVX/AVX2结构,支持16个256位的YMM寄存器

  • AVX-512,支持多达32个512位的ZMM寄存器

图1-1 AVX-512寄存器图

检测AVX指令

        在使用AVX之前,应用程序必须确定操作系统支持XGETBV指令、YMM寄存器状态,以及处理器支持使用XSAVE/XRSTOR和XRSTOR进行YMM状态管理AVX指令。步骤如下:

  1. 检查CPUID.1:ECX.OSXSAVE[bit 27] = 1 (启用XGETBV给应用程序使用)

  2. 执行XGETBV并验证 XCR0[2:1] = '11b'(xmm和ymm状态由操作系统启用)

  3. 检查CPUID.1:ECX.AVX[bit 28] = 1 (支持AVX指令)

    步骤3可以按照相对于1和2的任何顺序进行。

    其它指令集检测见官方手册。

图1-2 xcr0寄存器

内存对齐

SIMDData segment align(64)
    sseData     oword 0     ;64字节对齐
                align 32    ;对AVX数据进行对齐
    avxData     oword 0,1   ;32字节对齐
                align 64
    avx2Data    oword 0,1,2,3,4
    
SIMDData ends

        使用segment和ends伪指令创建段,用align(n)对齐,n必须为2的次幂。

        注意:对于试图访问未对齐到16字节的地址处的128位数据对象,几乎所有的SSE、AVX和AVX2指令都会生成内存对齐故障。应该始终确保SSE打包操作数要合理对齐。

SIMD指令

        SIMD数据移动:

  • (v)movd、(v)movq

movd    xmmn,reg32/mem32
movd    reg32/mem32,xmmn
​
movq    xmmn,reg64/mem64
movq    reg64/mem64,xmmn
movq    xmmn,xmmn
  • (v)movaps、(v)movapd、(v)movdqa 移动对齐打包单(双)精度、四字节对齐值

movaps  xmmn,mem128
movaps  xmmn,xmmn
vmovaps xmmn,mem128
vmovaps xmmn,xmmn
vmovaps ymmn,mem256
vmovaps ymmn,ymmn
  • (v)movups、(v)movupd、(v)movdqu 移动未对齐打包单(双)精度、四字节值

  • (v)movlps、(v)movlpd 移动低阶数据

  • movhps、movhpd 移动高阶数据

  • vmovhps、vmovhpd

vmovhps xmm(dest),xmm(src),mem64    ;高阶来自内存,低阶来自xmm寄存器
vmovhps xmm(dest),xmm(src)
​
vmovhpd xmm(dest),xmm(src),mem64
vmovhpd xmm(dest),xmm(src)
  • (v)movlhps 将低阶复制到高阶

movlhps     xmm(dest),xmm(src)
vmovlhps    xmm(dest),xmm(src),xmm(src2)
  • (v)movhlps 将高阶复制到低阶

  • (v)movshdup、(v)movsldup 移动奇(偶)索引并复制到xmm或ymm寄存器中

​图1-3 movshdup


  • (v)movddup 将低阶或内存单元复制到xmm或ymm寄存器中,并复制到高位

图1-4 movddup

  • (v)lddqu 操作与(v)movdqu相同

        混排解包:略。

        逻辑运算:

  • (v)andpd、(v)andndp

andpd   xmm(dest),xmm(src)/mem128
vandpd  xmm(dest),xmm(src1),xmm(src2),mem128
vandpd  ymm(dest),ymm(src1),ymm(src2),mem256
  • (v)orpd

  • (v)xorpd

        打包测试:(v)ptest

        字节移位指令:

  • (v)pslldq

  • (v)psrldq

    位移位指令:(v)shift

    SIMD整数加法:

  • (v)paddb、(v)paddw、(v)paddd、(v)paddq

paddb   xmm(dest),xmm/mem128
vpaddb  xmm(dest),xmm(src1),xmm(src2)/mem128
vpaddb  ymm(dest),ymm(src1),ymm(src2)/mem256
  • (v)paddsb、(V)paddsw、(v)paddusb、(v)paddusw 饱和加法

    整数水平运算:略。

    SIMD整数减法:

  • (v)psubb、(v)psubw、(v)psubd、(v)psubq

psubb   xmm(dest),xmm/mem128
vpsubb  xmm(dest),xmm(src1),xmm(src2)/mem128
vpsubb  ymm(dest),ymm(src1),ymm(src2)/mem256
  • (v)psubsb、(V)psubsw、(v)psubusb、(v)psubusw 饱和减法

    SIMD整数乘法:

  • (v)pmullw、(v)pmulhuw、(v)pmulhw

pmullw  xmm(dest),xmm/mem128
vpmullw xmm(dest),xmm(src1),xmm(src2)/mem128
vpmullw ymm(dest),ymm(src1),ymm(src2)/mem256
  • (v)pmulld、(v)pmullq

  • (v)pmuldq、(v)pmuludq

  • (v)pclmulqdq

    SIMD整数平均值:(v)pavgb、(v)pavgw

    SIMD最大最小值:略。

    SIMD整数绝对值:(v)pabsb、(v)pabsw、(v)pabsd

    SIMD整数符号调整:(v)psignb、(v)psignw、(v)psignd

    SIMD整数比较:

  • (v)pcmpeqb、(v)pcmpeqw、(v)pcmpeqd、(v)pcmpeqq

  • (v)pcmpgtb、(v)pcmpgtw、(v)pcmpgtd、(v)pcmpgtq

    整数转换:略

    SIMD浮点算数指令:

  • (v)addps、(v)addpd

  • (v)subps、(v)subpd

  • (v)mulps、(v)mulpd

  • (v)divps、(v)divpd

  • (v)maxps、(v)maxpd

  • (v)minps、(v)minpd

  • (v)sqrtps、(v)sqrtpd

  • (v)rsqrtps

        浮点水平运算:略。

        SSE和AVX比较指令:(v)cmpps、(v)cmppd

cmpps   xmm(dest),xmm(src)/mem128,imm8
vcmpps  xmm(dest),xmm(src1),xmm(src2)/mem128,imm8
vcmpps  ymm(dest),ymm(src1),ymm(src2)/mem256,imm8

        AVX扩展比较:vcmpps、vcmppd

        SIMD比较指令:(v)movmskps、(v)movmskpd

movmskps    reg,xmm(src)
vmovmskps   reg,ymm(src)

        浮点转换指令:略。

参数传递和返回值

表1-4 参数传递

参数类型第 5 个和更高位置第 4 个第3 个second最左侧
浮点堆栈XMM3XMM2XMM1XMM0
整型堆栈R9R8RDXRCX
聚合(8、16、32 或 64 位)和 __m64堆栈R9R8RDXRCX
其他聚合,作为指针堆栈R9R8RDXRCX
__m128,作为指针堆栈R9R8RDXRCX

        可以适应 64 位的标量返回值(包括 __m64 类型)是通过 RAX 返回的。 非标量类型(包括浮点类型、双精度类型和向量类型,例如 __m128__m128i__m128d)以 XMM0 的形式返回。 返回到 RAX 或 XMM0 中的值的未使用位数的状态未定义。

        推测:AVX传递使用RCX,RDX,R8,R9和堆栈,返回值使用YMM0。AVX-512返回使用ZMM0。

代码

cpp

//main.cpp
#include<iostream>
#include<stdio.h>
#include<intrin.h>

using namespace std;

extern "C"{
    int checkavx(void);
    char* cpuid0(int& maxFeature);
    __m256d dadd(__m256d a,__m256d b);
}

void print(__m256d md){
    for(int i=0;i<4;i++){
        cout<<md.m256d_f64[i]<<"\t";
    }
    cout<<endl;
}

int main(){
    if(checkavx()!=1){
        cout<<"not Support avx"<<endl;
        return 0;
    }
    char* vendorId=nullptr;
    __m256d a={{1.1,2.2,3.3,4.4}},b={{5.5,6.6,7.7,8.8}},c;
    int maxFeature=0;
    vendorId=cpuid0(maxFeature); 
    printf("CPUID(0): Vendor ID='%s', max feature=%d\n",vendorId,maxFeature);
    c=dadd(a,b);
    print(c);
    c=_mm256_mul_pd(a,b);
    print(c);
    c=_mm256_movedup_pd(a);
    print(c);
    c=_mm256_sqrt_pd(b);
    print(c);
    return 0;
}

asm

    ;avx.asm
        option casemap:none
    .data
    VendorID    byte    14 dup (0)

    .code 

    public checkavx
checkavx proc
    mov     eax,1
    cpuid
    and     ecx,018000000h
    cmp     ecx,018000000h
    jne     notSupport
    mov     ecx,0
    xgetbv
    and     eax,06h
    cmp     eax,06h
    jne     notSupport
    mov     eax,1
    jmp     done
notSupport:
    mov     eax,0
done:
    ret
checkavx endp

    public cpuid0
cpuid0 proc
    push    rbp
    mov     rbp,rsp
    push    rcx

    xor     eax,eax
    cpuid
    mov     r8,[rsp]
    mov     [r8],eax
    mov     dword ptr VendorID, ebx 
    mov     dword ptr VendorID[4], edx 
    mov     dword ptr VendorID[8], ecx
    mov     eax,offset VendorID

    leave 
    ret
cpuid0 endp

    public dadd
dadd proc
    vmovaps ymm1,[rcx]
    vmovaps ymm2,[rdx]
    vaddpd  ymm0,ymm1,ymm2
    ret
dadd endp

end

扩展资料

参考

  1. [美]兰德尔·海德.64位汇编语言的编程艺术

  2. Intel® 64 and IA-32 Architectures Software Developer’s Manual Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, and 4

  3. AMD64 Architecture Programmer’s Manual Volumes 1–5

  4. Intel® Intrinsics Guide

  5. x64 (amd64) 内部函数列表 | Microsoft Learn

  6. x64 调用约定 | Microsoft Learn

  7. x86 and amd64 instruction reference (felixcloutier.com)

  • 21
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值