【问题标题】:Can we say that an x86 CPU has data types?我们可以说 x86 CPU 有数据类型吗?
【发布时间】:2019-07-01 14:34:25
【问题描述】:

x86 CPU 有一些处理整数和浮点数的指令。

例如:INC 指令将整数(可以存储在内存或寄存器中)增加1,因此INC 指令“知道”它应该解释它正在操作的位作为整数。那么我们可以说 x86 CPU 有数据类型吗(就像我们可以说 C++ 有数据类型一样)?或者为了让我们能够这么说,x86 CPU 应该提供其他功能,如类型安全(它不提供)?

【问题讨论】:

  • 没有。 INC 增加内存位置或寄存器的内容。它对这些内容的类型没有概念(或关注),因为它们都是 CPU 的数字,无论该内容是指针、字符串的开头、字符还是整数。编译器提供类型安全。请参阅Raymond Chen's blog 了解一系列可能有助于澄清问题的文章。
  • @KenWhite:我不同意。拥有类型!= 拥有类型安全的类型系统。 x86 提供了几种可以本地操作的数据类型(x64 上的字节、字、dword、qword,movs 到 FPU 的第二个,以及各种浮点类型)。 inc 可以对这些整数数据类型进行操作,这些数据类型由目标操作数的编码指定(byte ptrword ptrdword ptrqword ptr)或隐含在寄存器的选择中。
  • @MatteoItalia:是的,但是INC指令只知道它在byte ptrword ptr等上运行;它不知道word ptr 指向什么,只知道它是word ptr。它是一个数值(一个word 大小的数字),它“应该是”某个东西的ptr - 这就是 CPU 所知道的全部。
  • @KenWhite:我想我们都知道inc 只是将操作数提供给二进制加法器。但我同意用“知道”来表达这一点似乎不准确。希望只是OP的笨拙措辞。我在回答中也说了同样的话。

标签: assembly types x86


【解决方案1】:

是的,asm 具有处理不同格式数据的操作,您可以调用这些类型。但是有零类型安全。这是一种很好的表达方式。

所以 INC 指令“知道”它应该将它正在操作的位解释为整数。

但这是一种笨拙的表达方式。 INC 什么都不“知道”;它只是将操作数提供给 ALU 中的二进制加法器。 完全由程序员(或编译器)在正确的字节上以正确的顺序使用正确的指令来获得所需的结果。例如实现具有类型的高级变量。

每个 asm 指令都按照它在锡上所说的做,不多也不少。指令集参考手册条目中的操作部分记录了它对机器架构状态的全部影响,包括 FLAGS 和可能的异常。例如inc。或者更复杂的指令,带有更有趣的伪代码,显示每个位的存放位置,BMI2 pdep r32a, r32b, r/m32(和图表)。从中提取这些内容的英特尔 PDF 有一个介绍部分,该部分解释了任何符号,例如 CF ← Bit(BitBase, BitOffset); for bts (bit test-and-set)


一切都只是字节(包括指针、浮点数、整数、字符串,甚至像 x86 这样的冯诺依曼架构中的代码)。 (或者在某些东西不是 1 字节的倍数的机器上,一切都只是位。)

没有什么能神奇地为您按类型宽度缩放索引。 (尽管 AVX512 在寻址模式中确实使用了缩放的disp8,因此 8 位位移可以编码高达矢量宽度的 -128..+127 倍,而不是只有那么多字节。在源代码级汇编中,您仍然可以编写字节偏移量,并由汇编器在可能的情况下使用更紧凑的机器代码编码。)

如果您想在指针的低字节上使用inc al 来循环遍历(对齐的)数组的前 256 个字节,那完全可以。 (在 P6 系列以外的 CPU 上效率很高,在读取完整寄存器时会出现部分寄存器停顿。)


在某种程度上,x86 对许多类型具有原生支持。大多数整数指令以字节、字、dword 和 qword 操作数大小 的形式出现。当然还有 FP 指令(float/double/long double),甚至还有大部分已经过时的 BCD 指令。

如果您关心有符号和无符号溢出,请分别查看 OF 或 CF。 (因此,有符号整数与无符号整数的区别在于,对于大多数指令,您在事后查看哪些标志,因为加/减对于无符号和 2 的补码来说是相同的二进制操作)。

但加宽乘法和除法确实有有符号和无符号版本。单操作数 imulmul(和 BMI2 mulx)进行有符号或无符号 N x N => 2N 位乘法。 (但通常您不需要高半结果,可以简单地使用更有效的imul r32, r/m32(或其他操作数大小)。乘法的低半部分与输入的有符号或无符号解释的二进制操作数相同; 只有高半部分不同,具体取决于输入的 MSB 是正位值还是负位值。)


使用与您正在实现的 C++ 数据类型相同的操作数大小并不总是一个好主意。例如8 位和 16 位通常可以使用 32 位操作数大小来计算,从而避免任何部分寄存器问题。对于加法/减法,进位仅从 LSB 传播到 MSB,因此您可以进行 32 位操作并且只使用结果的低 8 位。 (除非您需要右移或其他操作。)当然,cmp 的 8 位操作数大小会很方便,但这不会写入任何 8 位寄存器。


x86 数据类型/格式不仅仅包括整数

  • 有符号 2 的补码和无符号二进制整数
  • IEEE floatdouble,具有 SSE 和 SSE2 以及 x87 内存操作数。
  • 半精度 16 位浮点数(vcvtph2ps 和相反):仅加载/存储。一些 Intel CPUs have half-precision mul/add support in the GPU,但 x86 IA 内核只能转换以节省内存带宽并至少使用 float 进行矢量 FP 数学指令。
  • x87 的 80 位扩展精度
  • 80 位 BCD 与 x87 fbstp
  • 压缩和解压缩 BCD,由 AF 标志(半字节进位)和指令支持,如 DAA(压缩 BCD 十进制调整 AL 后加法)和 AAA(ASCII 调整后加法:用于 AL 中的解压缩 BCD,啊)。不在 64 位模式下
  • 带有bt/bts/etc 的位图:bts [rdi], eax 可以在rdi 的dword 之外选择位。与寄存器目标不同,位索引&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 位整数寄存器或内存位置。 imm8 是 8 位立即数。 (如果操作数大小大于 8,则通常符号扩展为操作数大小。)

手册将 m32fp 用于 x87 FP 内存操作数,而 m32int 用于 x87 fild / fistp(整数加载/存储)和其他整数-源 x87 指令,如 fiadd

还有诸如 m16:64 之类的东西,内存中的远指针(segment:offset),例如作为间接远jmp 或远call 的操作数。 计算远指针和 x86 支持的“类型”当然是​​合理的。 有类似 lgs rdi, [rsi] 的指令从 rsi 指向的 2+8 字节操作数加载 gs:rdi . (当然,更常用于 16 位代码。)

m128 / xmm 可能不是您真正所说的“数据类型”;没有 SIMD 指令实际上将操作数视为 128 位或 512 位整数。 64 位元素是除了随机播放之外的最大元素。 (或纯按位运算,但实际上是 128 个独立的并行与运算,相邻位之间根本没有交互。)

【讨论】:

  • 请注意,仍然存在寄存器宽度不是 8 倍数的字节导向机器,如 MSP430X 系统。您可能希望避免说“一切都只是字节”。
  • @fuz:很公平,一切都只是一点点:P
  • 我认为更正确的术语是“格式”。类型,如数学和 CS 中所理解的,是附加在数据上的标签,用于限制允许对数据进行的操作。所以我认为你不能拥有没有类型安全的类型。但是,是的,这是一条细线,值得商榷。
  • @MargaretBloom 也许“解释”是一个合适的词。
【解决方案2】:

只是一点点而已,仅此而已。 inc 操作的位可以是有符号整数、无符号整数,也可以是指向某个地址的指针。它甚至可能是一些聪明的(或相反的词)代码用来四舍五入尾数的浮点数。

如果对不同大小的位进行操作,例如乘法和除法,两个 8 位操作数会产生 16 位输出,对于二进制补码机器有符号的概念,无符号乘法和有符号乘法是不同的,它只是因为他们需要签署扩展操作数之一才能完成该操作。如果您输入 n 位并输出 n 位,那么您甚至都不关心符号,它仍然只是位。除法类似。

可以说浮点运算意味着位表示该格式,这是公平的。

但是 unsigned int vs char * vs float 等的概念主要存在于程序员的大脑中,在高级语言中,处理器非常非常愚蠢,他们接受他们输入的指令和数据位并对其进行操作程序员的工作最终是确保这些位是指令,数据是数据,并且它执行所需的任务。处理器只是一个位操作机器,每条指令的定义都被写下来,这样你就可以根据你输入的位知道你会得到什么位。

试图使汇编语言或机器代码具有类型大多是浪费时间,一些语法有诸如 mov word ptr 之类的东西,但这是指令集的本质,更重要的是汇编语言,其他语法可能有被使用,后来被用来生成正确的机器代码,而不使用字指针或 ptr 来简单地说明这是一种间接寻址模式。

试图在高级语言的上下文中理解汇编或机器代码并没有真正奏效,您必须尝试反过来思考。这些只是位,大多数语言都有描述这些位的类型,因此代码可以工作。有些语言甚至必须将相同的 8 位值从布尔值转换为整数或 (ASCII) 字符。只是为了让语言正常工作。

最简单的理解是 inc 或 add,如果您在高级语言中使用两个整数或一个整数和一个立即数并执行有意义的操作 hello = hello + 1;但是您可以从 char *x; 中分辨出该指令的区别。 ... x++;您仍然可以获得一些寄存器或内存引用以及添加中的立即数。处理器不知道也不关心一个是变量/整数,另一个是地址,它只是操作数和输出。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-12
    • 1970-01-01
    • 2014-05-17
    • 2013-12-04
    相关资源
    最近更新 更多