"comparison is for a two’s-complement ‘>=’" 是什么意思?

What does "comparison is for a two’s-complement ‘>=’" mean?

我正在学习汇编语言并且卡在了这一点上。这是“计算机系统”一书第3章的问题。 问题描述为:

1st part of the problem

2nd part of the problem

看问题A、B、C

A.

cmpl %eax, %edx
setl %al

解:后缀l和寄存器标识符表示32位操作数,而 比较是针对二进制补码“<”。我们可以推断 data_t 一定是 整数

B.

cmpw %ax, %dx
setge %al

解:后缀'w'和寄存器标识符表示16位操作数,而 比较是针对二进制补码“>=”。我们可以推断 data_t 必须 矮点。

C.

cmpb %al, %dl
setb %al

解:后缀'b'和寄存器标识符表示8位操作数,而 比较是针对无符号的“<”。我们可以推断 data_t 一定是 unsigned char.

我的问题是如何判断“比较是针对二进制补码‘<’”、“比较是针对二进制补码‘>=’”和“比较是针对无符号‘<’”。另外,我无法理解如何从中确定数据类型。

第一部分(数据类型)是straight-forward。 eax 是一个 32-Bit-register,所以数据类型是 int(或者更准确地说是 int32_t)。同样,ax 是一个 16 位寄存器,al 是一个 8 位寄存器。

对于第二部分,您需要了解说明。英特尔规范说(在 setxx 命令下):

The terms “above” and “below” are associated with the CF flag and refer to the relationship between two unsigned integer values. The terms “greater” and “less” are associated with the SF and OF flags and refer to the relationship between two signed integer values.

因此 setb 对无符号值进行运算,而 setlsetge 对有符号值进行运算。这里的“补码”与“有符号”的意思相同。

对于整数,数据类型的概念包括宽度和 signed-ness。

数据类型是变量类型,可以是有符号的也可以是无符号的。变量可以保存值,并且整数值可以是负数或正数。

汇编中有4种标准宽度,byte, word, long, quad,每一种都是prior的两倍。这些不一定表示是已签名还是未签名。在 x86 世界中,word 是 16 位,而在大多数其他环境中 (MIPS/RISC V) word 是指 32 位。此外,long 对于双字有时称为 dword,对于 8 字节值有时称为 qword

C 中有大约 4 种标准宽度,char、short、int、long,但在 C 中它们通常被理解为带符号的 — 除了 char 具有特定于实现的带符号性。 C 保证 sizeof(char) < sizeof(short) <= sizeof(int) <= sizeof(long),但要确切知道是什么,您必须查阅实现的文档——一个实现应该告诉您。许多实现都有 intlong 都是 32 位的,但有时有编译器选项可以改变它,并且 long long 通常是 64 位。

在C中,我们可以添加关键字signedunsigned来确保数据类型分别是可以容纳负值或不能容纳负值的数据类型。

对于广泛跨越有符号和无符号数据类型的比较操作,共有 10 个常用关系。现在让我们注意,编程语言和指令集省略了关系操作,其中一个操作数是有符号的,另一个是无符号的(反之亦然)。如果遇到这种情况,最好的方法是将两个操作数都提升到下一个更高的有符号大小,然后以这种方式进行比较。因此,包括在标准 10 关系中的是无符号到无符号和有符号到有符号的比较(但没有有符号到无符号,也没有无符号到有符号)。

其中两个等于 (==,eq) 和不等于 (!=,ne),对有符号和无符号数据类型都应用相同的 — 要相等或不同,位模式必须相同且有符号'ness 在那里并不重要(假设两个操作数都是有符号的或都是无符号的)。

对于其余部分,我们必须知道数据类型的符号性才能正确解释结果。一个负数,如果不小心被视为无符号数,看起来就像一个大的正数。因此,如果我们使用了错误的比较运算符,那么 -1 将显示为 maxint 并且大于 1。这就是为什么我们必须知道数据类型的原因。我们可以从比较运算符推断数据类型是有符号还是无符号。

业界普遍确定了术语:

  • 无符号 > 和无符号 <
  • 以上和以下
  • 无符号>= 和无符号<= (68000) 高于或相同&低于或相同
  • 无符号 >= 和无符号 <= 高于或等于 & 低于或等于(英特尔)
  • 小于无符号 (ltu) 无符号 < (MIPS/RISC V)
  • 小于和大于有符号 < 和有符号 >
  • 小于或等于 & 大于或等于有符号 <= 和有符号 >=

我们还要补充一点,C(和其他高级语言)使用逻辑变量声明来标记具有数据类型的变量,并且编译器生成的机器代码访问与该数据类型一致的相同变量的物理存储,无论何时程序使用变量。

而在机器代码中,没有处理器看到或知道的变量声明,因此,某些数据类型信息必须随每条根据需要操作存储的指令一起传送。要复制数据,处理器只需要知道大小,而不是符号,与 equal/not-equal 的比较相同,但对于其他操作(其他不等式,如 <、<= 或检测溢出)必须通知处理器数据类型的大小和签名。

处理器不读取变量声明至少有两个原因,一个是它们太难记住了,或者换句话说,我们有另一种记忆方式,那就是合并将该信息转化为程序的机器代码,这意味着程序真正知道,并在每条指令中告诉处理器。

另一个是处理器的物理存储:CPU 寄存器和内存经常被重新利用。 CPU 寄存器是永久性的,但高级语言的逻辑变量可能是短暂的——尤其是参数和局部变量。逻辑变量有作用域,当作用域退出时,变量消失,留下物理存储空间以供其他用途重用,汇编程序通过简单地用新值初始化物理存储空间来实现。因此,同一个寄存器一会儿可能保存一个无符号字节,另一时刻保存一个有符号整数。机器码程序的ob 是为了保持直截了当,编译器部分地通过它正在翻译的源代码中的类型声明来做到这一点。


条件分支有些复杂,如下。在 C 中,我们可能会做类似

if ( a < b ) goto Label;

这个相对简单的操作基本上有 4 个操作数,比大多数处理器在一条指令中容纳的要多。 4个操作数分别是变量1、变量2、特定关系运算符、goto-target标签。

因此,指令集设计者使用的一种方法是拆分 4 个操作数并将它们分散到 2 个单独的指令中,例如 comparebranchset。比较操作取变量 1 和变量 2 并同时进行所有 10 个比较操作,将所有 10 个结果放入标志寄存器。 branch 指令采用特定的关系运算符和 goto-target 标签——它们解释给定关系运算符的标志,看它是否应该分支。

setxx 指令与分支指令并行,但具有寄存器目标(布尔值)而不是 goto/branch 目标。