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