Nic*_*den 8 assembly x86-64 instruction-set
我正在通过《计算机系统从程序员的角度》(第3版)一书中介绍x86-64(以及一般而言的汇编)。根据网络上的其他来源,作者声明idivq
只采用一个操作数-就像这个声称的那样。但是随后,作者(在某些章节之后)给出了带有说明的示例idivq $9, %rcx
。
两个操作数?我首先以为这是一个错误,但从那本书开始就经常发生。
同样,应该从寄存器%rdx
(高阶64位)和%rax
(低阶64位)中的数量中获得红利-因此,如果在体系结构中定义了该数量,则似乎不可能指定第二个操作数股利。
这是一个练习的示例(懒得将其全部写下来-因此,图片是必经之路)。它声称idivq $9, %rcx
编译短C函数时会发出GCC 。
Pet*_*des 10
这是一个错误。只有imul
立即和 2 登记表。
mul、div 或 idiv 仍然仅以8086 引入的单操作数形式存在,使用 RDX:RAX 作为隐式双宽度操作数用于输出(和输入用于除法)。
或 EDX:EAX、DX:AX 或 AH:AL,当然取决于操作数大小。查阅 ISA 参考资料,如英特尔手册,而不是本书! https://www.felixcloutier.com/x86/idiv
另请参阅何时以及为什么我们签署扩展并将 cdq 与 mul/div 一起使用?而为什么要EDX使用DIV指令之前为0?
x86-64 唯一的硬件除法指令是idiv
和div
。删除了 64 位模式aam
,它按立即数进行 8 位除法。(在Assembler x86 中划分和在Assembly 中显示时间有一个aam
在16 位模式下使用的例子)。
当然,除以常数idiv
和div
(和aam
)是非常低效的。除非您针对代码大小而不是性能进行优化,否则请使用 2 的幂的移位,否则使用乘法逆运算。
CS:APP 3e 全球版在实践问题中显然有多个像这样的严重 x86-64 指令集错误,声称 GCC 发出不可能的指令。不仅仅是拼写错误或细微的错误,还有误导性的废话,这对于熟悉 x86-64 指令集的人来说显然是错误的。这不仅仅是一个语法错误,它试图使用不可编码的指令(没有语法可以表达它们,除了扩展为多个指令的宏。idivq
使用宏定义为伪指令会很奇怪) .
例如,我正确地猜到了函数的缺失部分,但是 gcc 生成的汇编代码与答案不匹配,这是另一个建议(%rbx, %rdi, %rsi)
并且(%rsi, %rsi, 9)
是有效寻址模式的答案!比例因子实际上是一个 2 位移位计数,所以这些都是垃圾,表明作者严重缺乏他们正在教授的 ISA 知识,而不是打字错误。
他们的代码不会用任何 AT&T 语法汇编器进行汇编。
另外这个只有一个操作数的 x86-64 addq 指令是什么意思?(来自 CSAPP book 3rd Edition)是另一个例子,他们在商店中有一个无意义的addq %eax
而不是inc %rdx
和不匹配的操作数大小mov
。
似乎他们只是在编造东西并声称它是由 GCC 发出的。IDK 如果他们从真正的 GCC 输出开始并将其编辑为他们认为更好的示例,或者实际上从头开始手写而不进行测试。
GCC 的实际输出将使用乘法与魔法常数(定点乘法逆)除以 9(即使在-O0
,但这显然不是调试模式代码。他们本可以使用-Os
)。
想必他们不想谈论为什么GCC在实现整数除法时使用乘以奇怪的数字?并用他们编写的指令替换了该代码块。从上下文中,您可能可以弄清楚他们期望输出的去向;也许他们的意思是rcx /= 9
。
来自出版商的网站 ( https://csapp.cs.cmu.edu/3e/errata.html )
关于全球版的说明:不幸的是,出版商在全球版中安排了一组不同的练习和家庭作业问题。做这件事的人做得不是很好,所以这些问题和他们的解决方案有很多错误。我们尚未为此版本创建勘误表。
所以CS:APP 3e大概是一本不错的教材,只要拿到北美版,或者忽略练习/作业问题。这解释了教科书的声誉和广泛使用与严重和明显的(对熟悉 x86-64 asm 的人来说)这样的错误之间的巨大脱节,这些错误超出了草率进入不知道语言领域的范围。
idiv reg, reg
或idiv $imm, reg
将此外,应从寄存器 %rdx(高位 64 位)和 %rax(低位 64 位)中的数量中给出除数——因此,如果在体系结构中定义了这一点,那么第二个操作数似乎不可能可以是指定的股息。
如果英特尔或AMD已经推出了新的便利表格div
或者idiv
,他们会设计的,它使用一个单一的宽红利,因为这是编译器如何一直使用它。
大多数语言都像 C 语言一样,将 + - * / 的两个操作数隐式提升为相同的类型并产生该宽度的结果。当然,如果已知输入很窄,则可以对其进行优化。(例如使用一个imul r32
来实现a * (int64_t)b
)。
但是div
,idiv
如果商溢出会出错,因此idiv
在编译int32_t q = (int64_t)a / (int32_t)b
.
编译器总是使用xor edx,edx
DIV或之前cdq
或cqo
IDIV之前实际做N / N => n比特除法。
使用不只是零或符号扩展的红利的真正全角除法只能通过内部函数或 asm 手动完成(因为 gcc/clang 和其他编译器不知道优化何时是安全的),或在 gcc 中在 32 位代码中执行例如 64 位 / 64 位除法的辅助函数。(或 64 位代码中的 128 位除法)。
因此,最有用的是 div/idiv,它也避免了设置 RDX 的额外指令,并最大限度地减少了隐式寄存器操作数的数量。(喜欢imul r32, r/m32
和imul r32, r/m32, imm
做:在没有隐式寄存器的情况下使非扩展乘法的常见情况更方便。这是像手册一样的英特尔语法,目的地优先)
最简单的方法是执行 2 操作数指令dst /= src
。或者可能用商和余数替换两个操作数。对BMI1andn
等 3 个操作数使用 VEX 编码,您可能有
idivx remainder_dst, dividend, divisor
. 第二个操作数也是商的输出。或者,您可以将余数写入 RDX,并使用商的非破坏性目标。
或者更有可能针对仅需要商idivx quot, dividend, divisor
而不将余数存储在任何地方的简单情况进行优化。idiv
当您需要商时,您始终可以使用常规。
BMI2mulx
使用隐式rdx
输入操作数,因为它的目的是允许多个带进位加法链用于扩展精度乘法。所以它仍然必须产生 2 个输出。但这个假设的新形式,idiv
将存在以节省代码大小和微指令周围正常使用的idiv
是没有扩大。所以 386imul reg, reg/mem
是比较点,而不是 BMI2 mulx
。
IDK 如果引入直接形式也有意义idivx
;您只会出于代码大小的原因使用它。乘法逆运算更有效地除以常数,因此这种指令在现实世界中的用例很少。
归档时间: |
|
查看次数: |
174 次 |
最近记录: |