【问题标题】:Why was no intrinsic access to the CPU's status register in the design of both C and C++?为什么在 C 和 C++ 的设计中都没有对 CPU 状态寄存器的内在访问?
【发布时间】:2012-09-22 09:39:10
【问题描述】:

在溢出标志的情况下,访问这个标志似乎对跨架构编程来说是一个巨大的福音。它将提供一种安全的替代方法来依赖未定义的行为来检查有符号整数溢出,例如:

if(a < a + 100) //detect overflow

我确实了解有安全的替代方案,例如:

if(a > (INT_MAX - 100)) //detected overflow

但是,C 和 C++ 语言似乎都缺少对状态寄存器或其中的各个标志的访问。为什么不包含此功能或做出了哪些语言设计决定禁止包含此功能?

【问题讨论】:

  • C 和 C++ 本来就是可移植的。并非所有处理器都具有相同的 CPU 状态寄存器。
  • SSE 操作没有溢出标志。允许编译器使用 SSE 指令来实现。
  • 无关紧要。就可移植性而言,是否有没有没有标志的 CPU 并不重要。只有可以或不能建造它们才有意义。
  • 在 C 最初设计的时候,还有其他事情。就像使用补码、符号和绝对值以及其他特性来实现有符号算术的 CPU 一样。所有这些都有完全不同的溢出语义。
  • @Timesquare “最初设计 C 时,没有可用的 SSE 指令” - 没错!在硬件以几秒钟的速度发展的时代(故意夸大其词),您不想要一种仅适用于当今硬件的语言。便携也意味着未来的证明。所以你可以说它确实影响了决定,即使不是直接影响。

标签: c++ c language-design


【解决方案1】:
  • 因为 C++ 被设计为一种可移植语言,即可以在许多 CPU(例如 x86、ARM、LSI-11/2、Game Boy、手机、冰柜、飞机、人体操作芯片和激光剑等设备)上编译的语言)。
    • 跨 CPU 的可用标志可能有很大不同
    • 即使在同一个 CPU 中,标志也可能不同(采用 x86 标量指令与矢量指令)
    • 某些 CPU 甚至可能根本没有您想要的标志
  • 必须回答这个问题:当编译器无法确定是否使用该标志时,是否应该始终提供/启用该标志?,这不符合仅付费对于你使用的东西 不成文但神圣的 C 和 C++ 法律
  • 因为必须禁止编译器进行优化,例如重新排序代码以保持这些标志有效

后者的示例:

int x = 7;
x += z;
int y = 2;
y += z;

优化器可能会将其转换为伪汇编代码:

alloc_stack_frame 2*sizeof(int)
load_int 7, $0
load_int 2, $1
add z, $0
add z, $1

这反过来会更类似于

int x = 7;
int y = 2;
x += z;
y += z; 

现在如果你查询中间的寄存器

int x = 7;
x += z;
if (check_overflow($0)) {...}
int y = 2;
y += z;

那么在优化和反汇编之后,你可能会这样结束:

int x = 7;
int y = 2;
x += z;
y += z;
if (check_overflow($0)) {...}

那么这是不正确的。

可以构建更多示例,例如常量折叠编译时间溢出。


旁注:我记得一个旧的 Borland C++ 编译器有一个小 API 来读取当前的 CPU 寄存器。但是,上面关于优化的论点仍然适用。

另一个旁注:检查溢出:

// desired expression: int z = x + y
would_overflow = x > MAX-y;

更具体

auto would_overflow = x > std::numeric_limits<int>::max()-y;

或者更好,不那么具体:

auto would_overflow = x > std::numeric_limits<decltype(x+y)>::max()-y;

【讨论】:

    【解决方案2】:

    因为 C 和 C++ 被设计为独立于平台的。状态寄存器不是。

    如今,二进制补码普遍用于实现有符号整数运算,但并非总是如此。一个人的补码或符号和绝对值曾经很常见。在最初设计 C 语言时,这种 CPU 仍在普遍使用。例如。 COBOL 区分负 0 和正 0,它们存在于那些架构上。显然,这些架构上的溢出行为是完全不同的!

    顺便说一句,你不能依赖未定义的行为来检测溢出,因为reasonable compilers 看到

    if(a < a + 100)
    

    会写一个警告并编译

    if(true)
    

    ...(前提是优化已开启且特定优化未关闭)。

    请注意,您不能依赖警告。仅当条件在等效转换后以true 或false 结束时,编译器才会发出警告,但在许多情况下,条件将在存在溢出的情况下被修改,而不会以普通true/false 结束.

    【讨论】:

      【解决方案3】:

      我能想到以下几个原因。

      1. 通过允许访问寄存器标志,语言跨平台的可移植性受到严重限制。

      2. 优化器可以彻底改变表达式,使你的标志无用。

      3. 这会使语言更复杂

      4. 大多数编译器都有大量内在函数,可以在不借助标志的情况下执行最常见的操作(例如带进位的加法)。

      5. 大多数表达式都可以以安全的方式重写以避免溢出。

      6. 如果您有非常特殊的需求,您可以随时退回到内联汇编

      似乎不需要访问状态寄存器来进行标准化工作。

      【讨论】:

        猜你喜欢
        • 2011-03-31
        • 1970-01-01
        • 1970-01-01
        • 2017-05-03
        • 1970-01-01
        • 1970-01-01
        • 2011-03-03
        • 2017-05-18
        • 2012-06-15
        相关资源
        最近更新 更多