微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

位移超出允许范围

如何解决位移超出允许范围

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

,

标准的作者试图将其行为可能不容易以与 C 抽象模型和目标平台上的一般操作原则一致的方式一致描述的任何行为,或者其行为可能可观察到的任何行为归类为未定义行为受优化影响。尽管几乎所有具有移位 N 指令的处理器都会通过计算 (xx<<y 将导致处理器花费数十亿个周期来执行单条指令,能够服务中断。虽然执行指令的时间通常不会被视为副作用,但在数十亿个周期内忽略中断可能会破坏系统稳定性。

对于那些移位指令总是以所描述的两种方式之一运行的架构,从来没有任何理由邀请通用编译器来做除以其中一种方式处理代码之外的任何事情,但标准没有区分行为对针对不寻常平台或试图标记代码可能与其不兼容的地方的实现做出让步,而不是邀请通用架构的通用编译器将行为先例抛诸脑后。

如果在没有令人信服的理由的情况下使用旨在维护长期行为先例的实现,而不考虑标准是否需要这种行为,则对 x

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。