位移超出允许范围

我们在生产中有一个代码,在某些情况下,它可能会将 32 位无符号整数左移超过 31 位。我知道这被认为是未定义的行为。不幸的是,我们现在无法解决这个问题,但我们可以解决这个问题,前提是我们可以假设它在实践中是如何工作的。

在 x86/amd64 上,我知道用于移位的处理器仅使用移位计数操作数的适当较低有效位。所以这a << b实际上相当于a << (b & 31). 从硬件设计来看,这是完全合理的。

我的问题是:这在现代流行的平台(例如 arm、mips、RISC 等)上如何在实践中工作。我的意思是在现代 PC 和移动设备中实际使用的那些,而不是过时或深奥的。

我们可以假设它们的行为方式相同吗?

编辑:

  1. 我正在谈论的代码目前在区块链中运行。它究竟如何工作并不重要,但至少我们希望确保它在所有机器上产生相同的结果。这是最重要的,否则可以利用它来诱导所谓的链分裂。

  2. 修复这意味着麻烦,因为修复应该同时应用于所有正在运行的机器,否则我们将再次面临链分裂的风险。但我们会在某个时候以有组织(受控)的方式来做这件事。

  3. 各种编译器的问题较小。我们只使用 GCC。我亲眼看了一下代码,里面有shl说明。坦率地说,鉴于上下文,我不希望它有任何不同(移位操作数来自任意来源,无法在编译时预测)。

  4. 请不要提醒我“不能假设”。我知道这个。我的问题是 100% 实用的。正如我所说,我知道在 x86/amd64 上,32 位移位指令仅占用位计数操作数的 5 个最低有效位。

这在当前的现代架构中表现如何?我们还可以将问题限制在 little-endian 处理器上。

回答

它是 C 中的 UB。这里有一个例子:https : //godbolt.org/z/5h9f7W6rr

除非您使用内联汇编,否则它没有任何实际用途。但是,如果您想了解特定平台如何处理左移汇编指令,则需要查看它们的汇编语言参考:

ARM-拇指



    If n is 32 or more, then all the bits in the result are cleared to 0.

    If n is 33 or more and the carry flag is updated, it is updated to 0. 

x86 - 以 32 为模进行移位。


回答

对于触发未定义行为的代码,编译器几乎可以做任何事情——嗯,这就是它未定义的原因——要求未定义代码的安全定义没有任何意义。理论评估或观察编译器翻译类似代码或关于“常见实践”可能是什么的假设不会真正给你答案。

评估编译器真正将您的 UB 代码翻译成什么可能是您唯一安全的选择。如果您想真正确定在极端情况下会发生什么,请查看生成的(汇编或机器)代码。现代调试器为您提供了捕获这些极端情况并告诉您实际发生的情况的工具集(毕竟,生成的机器代码是非常明确的)。这比推测编译器可能会发出什么代码要简单得多,也更安全。


以上是位移超出允许范围的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>