SSE

来自osdev
跳到导航 跳到搜索
实数、协处理器和向量单元
技术
X86 实现

流式单指令多数据扩展 (SSE-Streaming SIMD Extensions)

Streaming SIMD Extensions (SSE)

简介

奔腾III中引入了SSE,并为英特尔指令集额外提供了70条指令。 SSE指令可以帮助增加由于单指令,多数据 (SIMD) 指令的数据。 这些指令可以在多个数据上并行执行公共表达式。

SSE附带8个(64位模式下为16个)XMM寄存器(XMM0-7(15)),它们是128位寄存器。 某些SSE指令 (movntdqa,movdqa,movdqu等...) 可以在单个操作中从内存中加载16个字节或将16个字节存储到内存中。 此外,SSE还引入了一些非时态提示指令(movntdqa和movntdq),允许将一次性内存位置存储在非时态内存中,以便这些位置引用不会污染小型片上缓存。

由于此更改添加了新的寄存器,因此默认禁用,因为当时的典型操作系统还不能将这些寄存器保存在任务交换机上。 为了支持SSE,您需要实现单独的代码路径来保存和恢复SSE状态 (因为这些指令将在不支持它的处理器上导致异常),以及新异常的处理程序。 之后,您可以告诉CPU在userland任务中启用SSE使用。

检查SSE

要检查SSE CPUID.01h: 需要设置EDX.SSE[bit25]

mov eax, 0x1
cpuid
test edx, 1<<25
jz .noSSE
;SSE is available

增加支持

为了允许执行SSE指令而不生成#UD,我们需要更改CR0和CR4寄存器。

清除 CR0.EM bit (bit 2) [ CR0 &= ~(1 << 2) ]
设置 CR0.MP bit (bit 1) [ CR0 |= (1 << 1) ]
设置 CR4.OSFXSR bit (bit 9) [ CR4 |= (1 << 9) ]
设定 CR4.OSXMMEXCPT bit (bit 10) [ CR4 |= (1 << 10) ]

这是一个asm示例:

;now enable SSE and the like
mov eax, cr0
and ax, 0xFFFB		; 清除协处理器仿真CR0.EM
or ax, 0x2			;设置协处理器监控CR0.MP
mov cr0, eax
mov eax, cr4
or ax, 3 << 9		; 同时设置CR4.OSFXSR和CR4.OSXMMEXCPT
mov cr4, eax
ret

FXSAVE 和 FXRSTOR

FXSAVE和FXRSTOR用于从内存中保存和加载完整的SSE、x87 FPU和MMX状态。 主机需要为存储分配512字节,并将该内存指针用作FXSAVE或FXRSTOR的操作数。 在使用这两条指令之前,请确保检查FXSR位的CPUID功能。 另外,像大多数SSE指令一样,内存操作数需要对齐16字节,否则将发生#GP异常。 记住在*任何MXCSR修改发生之前执行FXSAVE*,否则寄存器很可能会被覆盖或根据未知状态MXCSR_MASK设置为0。

示例用法:

char fxsave_region[512] __attribute__((aligned(16)));
asm volatile(" fxsave %0 "::"m"(fxsave_region));

or in asm:

segment .code
SaveFloats:
fxsave [SavedFloats]
segment .data
align 16
SavedFloats: TIMES 512 db 0

陷阱: 仅支持一级保存。

MXCSR及其助手LDMXCSR和STMXCSR

MXCSR寄存器保存用于SSE浮点运算的所有掩码和标志信息。 就像x87 FPU控制字一样,如果您想屏蔽某些异常或想指定舍入类型,则需要修改MXCSR。 位16-31是保留的,如果设置,将导致#GP异常。 LDMXCSR和STMXCSR分别加载和写入MXCSR寄存器。 它们都需要一个32位的内存操作数。 在使用这些指令(CR4.OSFXSR=1、CR0.EM=0和CR0.TS=0)之前,需要已经设置SSE支持。 如果设置了7-12位,则所有SSE浮点异常都将被屏蔽。 位0-5是异常状态标志,在发生相应异常时设置。 位13-14是RC(舍入控制)位。 RC:0 = 取最近,RC:1 = 向下取,RC:2 = 向上取,RC:3 = 截断。

更新到SSE

后来的处理器为要在向量寄存器(vector registers)上执行的不同工作添加了更多指令。 在适当的SSE支持下支持它们不需要操作系统方面的任何努力 (AVX除外,请参见下文)。 然而,指令的实际用户应该检查这些指令是否确实存在。

CPUID位

SSE2

Streaming SIMD Extensions 2 (SSE2)
SSE2的位可以在CPUID page 1的EDX位26中找到。

SSE3

Streaming SIMD Extensions 3 (SSE3)
SSE3的位可以在CPUID page 1的ECX位0中找到。

SSSE3

Supplemental Streaming SIMD Extensions 3 (SSSE3)
SSSE3的位可以在CPUID page 1的ECX位9中找到。

SSE4

Streaming SIMD Extensions 4 (SSE4)
SSE4.1的位可以在CPUID page 1的ECX位19中找到

SSE4.2的位可以在CPUID page 1的ECX位20中找到

SSE4A的位可以在CPUID page 1的ECX位6中找到

SSE5

Streaming SIMD Extensions 5 (SSE5)
SSE5计划作为一个单元,但分成了几个:

XOP

XOP的位可以在CPUID page 1的ECX位11中找到

FMA4

FMA4的位可以在CPUID page 1的ECX位16中找到

CVT16

CVT16的位可在CPUID page 1的ECX位29中找到

AVX

AVX的位可以在CPUID page 1的ECX位28中找到

XSAVE

XSAVE (管理扩展处理器状态所需) 的位可以在CPUID page 1的ECX位26中找到

AVX2

The bit for AVX2 can be found on CPUID page 7, 0, in EDX bit 26

AVX-512

The bits for AVX-512 are in CPUID page 0x0D, 0x0, EAX bits 5-7

X86_64

X86-64体系结构推出时,AMD要求最低级别的SSE支持,以简化操作系统代码。 任何支持长模式的系统都应该至少支持SSE和SSE2,这意味着内核不需要关心旧的FPU保存代码。 X86-64将8个SSE寄存器 (xmm8 - xmm15) 添加到混合中。 但是,您只能在64位模式下访问这些。

Advanced Vector Extensions(高级矢量扩展)是英特尔于2011年推出的‘’SIMD‘’(单指令、多数据)指令集。

AVX

AVX需要内核启用后才能使用。 忘记这样做会在第一次AVX通话时引发#UD。 在允许之前,必须同时启用SSE和OSXSAVE。 如果不这样做,也会产生一个#UD。

AVX通过设置XCR0寄存器的第2位启用。 还必须设置XCR0的位1(表示支持SSE)。

以下是启用SSE后启用AVX的汇编代码示例 (您应该首先检查AVX和XSAVE是否支持,请参见上文):

enable_avx:
    push rax
    push rcx
    push rdx

    xor rcx, rcx
    xgetbv ;Load XCR0 register
    or eax, 7 ;Set AVX, SSE, X87 bits
    xsetbv ;Save back to XCR0

    pop rdx
    pop rcx
    pop rax
    ret

要启用AVX-512,请设置XCR0的OPMASK(位5)、ZMM_Hi256(位6)、Hi16_ZMM(位7)。 您必须首先确保这些位有效(见上文)。

另见

参考资料