x86 CPU的某些指令处理整数和浮点数。
例如:INC
指令以1
递增一个整数(可以存储在存储器或寄存器中),因此INC
指令“知道”它应该解释它以整数形式操作的位。那么我们能不能说一个x86 CPU具有数据类型(就像我们可以说C ++具有数据类型一样)?还是为了使我们能够这样说,x86 CPU应该提供其他功能,例如类型安全(它不提供)?
答案 0 :(得分:10)
是的,asm可以处理不同格式的数据,您可以调用这些类型。但是安全类型为零。这是表达它的好方法。
因此,INC指令“知道”它应该将要操作的位解释为整数。
但这是表达这一点的笨拙方式。 INC什么都不知道;它只是将操作数提供给ALU中的二进制加法器。 完全由程序员(或编译器)使用正确的指令,在正确的字节上获得正确的结果。 实现具有类型的高级变量。
每条asm指令都按照罐子上的指示行事,不多也不少。指令集参考手册条目中的“操作”部分记录了它对机器的体系结构状态(包括FLAGS和可能的异常)的全部影响。例如inc
。或更复杂的指令带有更有趣的伪代码,它们显示每个位的存放位置BMI2 pdep r32a, r32b, r/m32
(和图)。这些是从中提取的Intel PDF的简介部分,介绍了bts
(bit test-and-set)
CF ← Bit(BitBase, BitOffset);
一切都只是字节(包括指针,浮点数,整数,字符串,甚至是x 86等冯·诺依曼体系结构中的代码)。 (或者在某些东西不是1字节的倍数的机器上,一切都只是位。)
没有什么能为您神奇地按类型宽度缩放索引。 (尽管AVX512在寻址模式下确实使用了缩放的disp8
,所以8位位移最多可以编码-128 .. + 127倍矢量宽度,而不是那么多字节。在源代码级汇编中,您可以仍然会写字节偏移量,这取决于汇编程序在可能时使用更紧凑的机器代码编码。)
如果要在指针的低字节上使用inc al
来循环(对齐)数组的前256个字节,那完全没问题。 (并且在除P6-系列以外的CPU上非常有效,在读取完整寄存器时会出现部分寄存器停顿的情况。)
在某种程度上,x86对多种类型具有本机支持。大多数整数指令以字节,字,双字和qword 操作数大小的形式出现。当然,还有FP指令(float
/ double
/ long double
),甚至是过时的BCD指令。
如果您关心有符号与无符号溢出,则分别查看OF或CF。 (因此,对于大多数指令而言,带符号对无符号整数取决于事实,您要看哪个标志,因为对于无符号和2的补码,add / sub是相同的二进制运算。)
但是扩大乘法和除法确实有符号和无符号版本。单操作数imul
与mul
(和BMI2 mulx
)进行有符号或无符号N x N => 2N位乘法。 (但通常您不需要上半部分的结果,而可以简单地使用更有效的imul r32, r/m32
(或其他操作数大小)。乘法的下半部分是用于以下项的有符号或无符号解释的相同二进制操作数:输入;只有高半部分取决于输入的MSB是正数还是负数。)
使用与要实现的C ++数据类型相同的操作数大小并不总是一个好主意。例如通常可以使用32位操作数大小来计算8位和16位,从而避免任何部分寄存器问题。对于加/减,进位仅从LSB传播到MSB,因此您可以执行32位运算,并且仅使用结果的低8位。 (除非您需要右移或其他操作。)当然cmp
的8位操作数大小很方便,但这不会写入任何8位寄存器。 / p>
x86数据类型/格式不仅仅包含整数
float
和double
,带有SSE和SSE2,以及x87内存操作数。vcvtph2ps
及相反):仅加载/存储。某些Intel CPUs have half-precision mul/add support in the GPU,但x86 IA内核只能转换以节省内存带宽,并且至少要使用float
来处理矢量FP数学指令。fbstp
的80位BCD bt
/ bts
/ etc:<{1}}的bts [rdi], eax
的dword的外部位。与寄存器目标不同,位索引不是用rdi
(https://www.felixcloutier.com/x86/bt)掩盖的。 (这就是&0x1f
如此之多而reg,reg和mem,immediate不错的原因。)另请参阅How to read the Intel Opcode notation,以获取英特尔指令集参考手册中使用的所有符号的列表。例如 r / m8 是8位整数寄存器或存储器位置。 imm8 是8位立即数。 (如果大于8,通常将符号扩展为操作数大小。)
该手册将 m32fp 用于x87 FP内存操作数,将 m32int 用于x87 bt/bts/etc mem,reg
/ fild
(整数加载/存储),以及其他fiadd
之类的整数源x87指令。
还有诸如 m16:64 之类的东西,它是内存中的远指针(段:偏移量),例如作为间接远fistp
或远jmp
的操作数。 对远指针和x86支持的“类型”进行计数当然是合理的。类似lgs rdi, [rsi]
这样的指令会从指向的2 + 8字节操作数中加载call
由gs:rdi
。 (当然,更常用的是16位代码。)
m128 / xmm 可能不是您真正称呼的“数据类型”;没有SIMD指令实际将操作数视为128位或512位整数。 64位元素是除洗牌之外最大的元素。 (或者是纯按位运算,但实际上是并行的128个独立的AND运算,相邻位之间完全没有交互。)
答案 1 :(得分:0)
这只是一点点,仅此而已。 inc正在操作的位可以是有符号整数,无符号整数,也可以是指向某个地址的指针。甚至可能是一些聪明的(或相反的单词)代码用来对尾数进行四舍五入的浮点数。
一些指令,例如乘法和除法,如果对不同大小的位进行运算,则产生两个8位操作数,从而产生16位输出,具有二进制补码机的符号概念,无符号乘法和有符号乘法是不同的,并且仅仅是因为它们需要对其中一个操作数进行符号扩展才能完成该操作。如果您进行n位输入和n位输出,那么您甚至不必关心符号,它仍然只是位。鸿沟很相似。
可以说浮点运算意味着这些位代表该格式,这很公平。
但是,无符号int vs char * vs float等概念主要在于程序员的大脑和高级语言,处理器非常愚蠢,它们接受被馈送指令和数据的位并对其进行操作最终,程序员的工作是确保这些位是指令,而数据是数据,并且它执行所需的任务。处理器只是位操纵机器,它记录了每条指令的定义,以便您根据喂入的位知道要取出哪些位。
尝试使汇编语言或机器代码具有类型通常是在浪费时间,某些语法具有诸如mov word ptr之类的东西,但这是指令集的性质,更重要的是汇编语言,其他语法可能具有后来被用来获取正确的机器代码,而无需使用单词指针或ptr来简单说明这是一种间接寻址模式。
尝试在高级语言的上下文中理解汇编或机器代码并不能真正起作用,您必须尝试以另一种方式思考。这些只是位,大多数语言都有描述这些位的类型,因此代码可以正常工作。某些语言甚至具有相同的8位值,因此必须将其从布尔值转换为整数或(ASCII)字符。只是为了使语言有效。
最容易理解的是inc或加号,如果您使用高级语言中的两个整数或一个整数与一个立即数进行有意义的操作hello = hello + 1;但是您可以从char * x看出该指令的区别; ... x ++;您仍然会获得一些寄存器或内存引用,以及添加的立即数。处理器不知道也不关心一个是变量/整数,另一个是地址,它只是操作数和输出。