优化汇编例程(2)

3. 汇编编程基础

3.1. 可用的汇编器

X86指令集有几个汇编器可用,但目前它们没有一个好到被一致推荐。汇编程序员处在x86汇编没有统一语法的不幸境地之中。不同的汇编器使用不同的语法变体。最常用的汇编器有如下。

MASM

这个Microsoft汇编器包括在Microsoft C++编译器中。有时可以通过下载Microsoft Windows驱动套装(WDK),或平台软件开发套装(SDK),或作为免费Visual C++精简版的扩展,获得免费版本。在Windows世界里,MASM已经成为事实上的标准许多年,大多数Windows编译器的汇编输出使用MASM语法。MASM有许多先进的语言特性。由于继承自用于8086处理器的最早期汇编器,语法有点凌乱且不一致。Microsoft仍然在维护MASM,以对Windows提供一套完整的开发工具,但这显然无利可图,MASM的维护被保持在最低限度。新指令集仍然被定期添加,但64位版本有几个缺陷。更新的版本仅在安装了编译器时可以运行,且仅在Windows XP或更新系统上。版本6与更早的版本可以运行在任何系统上,包括使用Windows模拟器的Linux。这样的版本在网上流传。

GAS

Gnu汇编器是包括在大多数Linux,BSD及Mac OS X发布的Gnu Binutils包的部分。Gnu编译器产生的汇编输出,在链接前经过Gnu汇编器。传统上,Gnu汇编器使用对机器生成代码工作良好的AT&T语法,但它产生的汇编对人非常不方便。AT&T语法中操作数的次序,与所有其他x86汇编器,以及Intel与AMD公布的指令文档给出的,都不一样。它使用各种前缀,像%与$来说明操作数类型。Gnu汇编器对所有x86平台都可用。

幸运地,更新版本的Gnu汇编器有一个使用Intel语法的选项。Gnu-Intel语法几乎与MASM语法完全相同。Gnu-Intel语法仅定义了指令代码的语法,没有指示、函数、宏等。指示仍然使用旧的Gnu-AT&T语法。指定.intel_syntax noprefix来使用Intel语法。在离开C或C++代码中的内联汇编时,指定.att_syntax prefix返回AT&T语法。

NASM

NASM是一个免费、开源的汇编器,支持多个平台与目标文件格式。相比MASM语法,其语法更清晰、更一贯。NASM定期更新指令集。NASM比MASM有更少的高级特性,但对大多数目的,它已足够。我将推荐NASM作为一个非常好的多平台汇编器。

YASM

YASM非常类似于NASM,使用完全相同的语法。在某些时期,YASM是第一个支持新指令集的,在其他时期是NASM。YASM与NASM可互换使用。

FASM

FASM汇编器是另一个开源的多平台汇编器。其语法不与其他汇编器兼容。FASM本身以汇编写就——一个诱人的想法,但不幸的是,这使得它的开发与维护不那么有效率。

WASM

WASM汇编器包含在Open Watcom C++编译器中。其语法类似MASM,但有些不同。没有紧跟时代。

JWASM

JWASM是WASM的进一步开发。它完全与MASM语法兼容,包括先进的宏与高级指示。如果要求MASM语法,JWASM是一个好的选择。JWASM可用一个称为Easy Code的集成开发环境(IDE)。

TASM

Borland Turbo汇编器包含在CodeGear C++ Builder里。它与MASM语法兼容,除了一些较新的语法补充。TASM不再维护,但仍然可用。它过时了,不支持当前的指令集。

GOASM

GoAsm是用于32与64位Windows的免费汇编器,包括资源编译器,链接器与调试器。其语法类似于MASM,但不完全兼容。目前,它没有支持最新的指令集。一个称为Easy Code的集成开发环境(IDE)也是可用的。

HLA

High Level Assembler实际上是一个支持类似汇编语句,产生汇编输出的高级语言编译器。在发明它的时候,这可能是一个好主意,但今天最好的C++编译器都支持固有函数,我相信不再需要HLA。

内联汇编

Microsoft与Intel C++编译器支持使用MASM语法子集的内联汇编。只要在汇编代码中插入名字,访问C++变量、函数及标签是可能的。这很容易,但不支持C++寄存器变量。参考第29页。

Gnu编译器支持可访问所有指令以及Gnu汇编器Intel与AT&T语法形式指示的内联汇编。从汇编访问C++变量,使用相当复杂的方法。

用于Linux与Mac系统的Intel编译器支持Microsoft与Gnu形式的内联汇编。

C++中的固有函数

这是组合低级代码与高级代码最新以及最便利的方式。固有函数是机器指令的高级语言表示。例如,可以通过调用等效于向量加法汇编指令的固有函数,在C++中进行一个向量加法。另外,定义一个带有重载+操作符的向量类,使只要通过写+就能进行向量加法,是可能的。Microsoft,Intel,Gnu与Clang编译器都支持固有函数。参考第27页与手册1《优化C++软件》。

选择哪个汇编器?

在大多数情形里,最简单的解决方案是在C++代码中使用固有函数。编译器可以负责大部分优化,使程序员可以集中精力在选择最好的算法,并把数据组织为向量。系统程序员可以通过使用固有函数访问系统指令,无需使用汇编语言。

在真正需要低级编程时,比如在高度优化的函数库或设备驱动中,你可以使用汇编器。

最好使用一个与你使用的C++编译器兼容的汇编器。这使你可以使用这个编译器把C++翻译为汇编,进一步优化汇编代码,然后汇编之。如果汇编器与编译器产生的语法不兼容,那么可以通过这个编译器产生一个目标文件,把目标文件反汇编为你需要的汇编语法。Objconv反汇编器支持几个不同的语法方言。

对许多用途,NASM汇编器都是好的选择,因为它支持许多平台与目标文件格式,它有良好的维护,经常更新到最新的指令集。

本手册中的例子使用MASM语法,除非另外指出。MASM语法在msdn.microsoft.com上的《Microsoft Macro Assembler Reference》中描述。

各种语法手册、编程手册与论坛的链接,参考www.agner.org/optimize

    1. 寄存器集与基本指令

16位模式中的寄存器

通用与整数寄存器

完整寄存器

比特 0 - 15

部分寄存器

比特 8 - 15

部分寄存器

比特 0 - 7

AX

AH

AL

BX

BH

BL

CX

CH

CL

DX

DH

DL

SI

 

 

DI

 

 

BP

 

 

SP

 

 

Flags

 

 

IP

 

 

表3.1. 16位模式中的通用寄存器

如果微处理器与操作系统支持,在16位模式中,32位寄存器也可用。不应该使用ESP的高位字,因为在中断期间,它不会被保存。

浮点寄存器

完整寄存器,比特 0 - 79

ST(0)

ST(1)

ST(2)

ST(3)

ST(4)

ST(5)

ST(6)

ST(7)

表3.2. 浮点栈寄存器

如果微处理器支持,MMX寄存器可能可用。如果微处理器与操作系统支持,XMM寄存器可能可用。

段寄存器

完整寄存器,比特0 - 15

CS

DS

ES

SS

表3.3. 16位模式中的段寄存器

寄存器FS与GS可能可用。

32位模式中的寄存器

通用与整数寄存器

完整寄存器

比特 0 - 31

部分寄存器

比特 0 - 15

部分寄存器

比特 8 - 15

部分寄存器

比特 0 - 7

EAX

AX

AH

AL

EBX

BX

BH

BL

ECX

CX

CH

CL

EDX

DX

DH

DL

ESI

SI

 

 

EDI

DI

 

 

EBP

BP

 

 

ESP

SP

 

 

EFlags

Flags

 

 

EIP

IP

 

 

表3.4. 32位模式中的通用寄存器

浮点与64位向量寄存器

完整寄存器,比特 0 - 79

部分寄存器,比特 0 - 63

ST(0)

MM0

ST(1)

MM1

ST(2)

MM2

ST(3)

MM3

ST(4)

MM4

ST(5)

MM5

ST(6)

MM6

ST(7)

MM7

表3.5. 浮点与MMX寄存器

如果微处理器支持,MMX寄存器才可用。ST与MMX寄存器不能用在代码的同一部分。使用MMX寄存器的代码部分,与使用ST寄存器的后续部分,必须通过EMMS指令隔开。

128与256位整数与浮点向量寄存器

完整或部分寄存器

比特 0 - 127

完整或部分寄存器

比特 0 - 255

完整

比特 0 - 511

XMM0

YMM0

ZMM0

XMM1

YMM1

ZMM1

XMM2

YMM2

ZMM2

XMM3

YMM3

ZMM3

XMM4

YMM4

ZMM4

XMM5

YMM5

ZMM5

XMM6

YMM6

ZMM6

XMM7

YMM7

ZMM7

表3.6. 32位模式中的XMM,YMM与ZMM寄存器

仅在微处理器与操作系统都支持,XMM才可用。对单精度或双精度,标量浮点指令分别仅使用XMM寄存器的32或64位。仅在处理器与操作系统都支持AVX指令集时,YMM寄存器才可用。仅处理器支持AVX-521指令集时,ZMM寄存器可用。

段寄存器

完整寄存器,比特 0 - 15

CS

DS

ES

FS

GS

SS

表3.7. 32位模式中的段寄存器

64位模式中的寄存器

通用与整数寄存器

完整寄存器

比特 0 - 63

部分寄存器

比特 0 - 31

部分寄存器

比特 0 - 15

部分寄存器

比特 8 - 15

部分寄存器

比特 0 - 7

RAX

EAX

AX

AH

AL

RBX

EBX

BX

BH

BL

RCX

ECX

CX

CH

CL

RDX

EDX

DX

DH

DL

RSI

ESI

SI

 

SIL

RDI

EDI

DI

 

DIL

RBP

EBP

BP

 

BPL

RSP

ESP

SP

 

SPL

R8

R8D

R8W

 

R8B

R9

R9D

R9W

 

R9B

R10

R10D

R10W

 

R10B

R11

R11D

R11W

 

R11B

R12

R12D

R12W

 

R12B

R13

R13D

R13W

 

R13B

R14

R14D

R14W

 

R14B

R15

R15D

R15W

 

R15B

RFlags

 

Flags

 

 

RIP

 

 

 

 

表3.8. 64位模式中的寄存器

高8位寄存器AH,BH,CH,DH仅可以用在没有REX前缀的指令中。注意修改一个32位部分寄存器将寄存器余下部分(比特32 ~ 63)置零,但修改一个8位或16位部分寄存器不会影响该寄存器余下部分。这可由以下序列来展示:

; Example 3.1. 8, 16, 32 and 64 bit registers

mov rax, 1111111111111111H          ; rax = 1111111111111111H

mov eax, 22222222H                           ; rax = 0000000022222222H

mov ax, 3333H                                      ; rax = 0000000022223333H

mov al, 44H                                            ; rax = 0000000022223344H

这个不一致有一个很好的理由。将寄存器不使用的部分置零,比不改变它更高效,因为这消除了对之前值的一个假依赖。但重置寄存器未使用部分的原则不能扩展到16位及8位部分寄存器,因为这将破坏与32位及16位模式的前向兼容。

唯一可以有64位立即数的指令是MOV。其他整数指令仅能有32位符号扩展操作数,例如:

; Example 3.2. Immediate operands, full and sign extended

mov rax, 1111111111111111H           ; Full 64 bit immediate operand

mov rax, -1                                              ; 32 bit sign-extended operand

mov eax, 0ffffffffH                                 ; 32 bit zero-extended operand

add rax, 1                                                ; 8 bit sign-extended operand

add rax, 100H                                         ; 32 bit sign-extended operand

add eax, 100H                                         ; 32 bit operand. result is zero-extended

mov rbx, 100000000H                           ; 64 bit immediate operand

add rax, rbx                                             ; Use an extra register if big operand

使用16位符号扩展操作数是不可能的。如果需要向一个64位寄存器加一个立即数,如果这个值太大,不能放入32位符号扩展操作数,必须首先将这个值移入另一个寄存器。

浮点与64位向量寄存器

完整寄存器,比特 0 - 79

部分寄存器,比特 0 - 63

ST(0)

MM0

ST(1)

MM1

ST(2)

MM2

ST(3)

MM3

ST(4)

MM4

ST(5)

MM5

ST(6)

MM6

ST(7)

MM7

表3.9. 浮点与MMX寄存器

ST与MMX寄存器不能用在代码的同一个部分。使用MMX寄存器的代码部分必须通过一条EMMS指令与使用ST寄存器的后续部分隔开。对64位Windows,ST与MMX寄存器不能用在设备驱动中。

128位与256位整数与浮点向量寄存器

完整或部分寄存器

比特 0 - 127

完整或部分寄存器

比特 0 - 255

完整寄存器

比特 0 - 511

XMM0

YMM0

ZMM0

XMM1

YMM1

ZMM1

XMM2

YMM2

ZMM2

XMM3

YMM3

ZMM3

XMM4

YMM4

ZMM4

XMM5

YMM5

ZMM5

XMM6

YMM6

ZMM6

XMM7

YMM7

ZMM7

XMM8

YMM8

ZMM8

XMM9

YMM9

ZMM9

XMM10

YMM10

ZMM10

XMM11

YMM11

ZMM11

XMM12

YMM12

ZMM12

XMM13

YMM13

ZMM13

XMM14

YMM14

ZMM14

XMM15

YMM15

ZMM15

ZMM16

 

 

ZMM17

 

 

ZMM18

 

 

ZMM19

 

 

ZMM20

 

 

ZMM21

 

 

ZMM22

 

 

ZMM23

 

 

ZMM24

 

 

ZMM25

 

 

ZMM26

 

 

ZMM27

 

 

ZMM28

 

 

ZMM29

 

 

ZMM30

 

 

ZMM31

 

 

表3.10. 64位模式中的XMM,YMM与ZMM寄存器

对单精度或双精度,标量浮点指令仅分别使用XMM寄存器的32或64位。仅当处理器与操作系统支持AVX指令集时,YMM寄存器才可用。仅当处理器支持AVX512指令集时,ZMM寄存器才可用。在处理器与汇编器都支持AVX512时,才可能使用XMM16-31与YMM16-32。

段寄存器

完整寄存器,比特 0 - 15

CS

FS

GS

表3.11. 64位模式中的段寄存器

段寄存器仅用于特殊目的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值