什么 – 或做 – “挥发性无效function(……)”做什么?

从语法的角度来看,我已经看过“volatile”关键字在C ++函数中有多少用法? 关于在函数上使用volatile关键字,但没有明确解释该问题的案例1做了什么。 只有一位受访者表示这似乎毫无意义/无用。

然而,我不能完全接受这种说法,因为GNUC的AES软件实现已经使用了几年,它们有许多这样的function:

INLINE volatile void functionname( /* ... */ ) { /* ... */ asm( /* ... */ ) // embedded assembly statements /* ... */ } 

必须有这种用法的原因。 谁能:

A. 告诉我原来的原因是什么; 和

B。 现在如何达到预期的效果?

我正在使用Ubuntu和GCC 4.6.3。


注意:我最接近解释的是,在GCC 2.5之前,您可以通过以下方式欺骗在2.5中实现的’noreturn’属性:

 void fatal( /* ... */ ) { /* ... */ exit(1); } typedef void voidfn (); volatile voidfn fatal; 

这将允许编译器识别’致命’不会返回。

但是这种情况似乎不适用于AES代码。 自从我在集会上做了很多事以来已经很长时间了,但我想我会认识到跳跃或类似的事情。

根据gcc文档(直到2015年2月) , volatile void作为C中的函数返回值(但不是在C ++中)等效于__attribute__((noreturn))上的__attribute__((noreturn)) ,并告诉编译器函数永远不会返回。

C99标准在§6.7.3/ 3中说明了这一点:

与限定类型关联的属性仅对作为左值的表达式有意义。 114)

114)实现可以在只读存储区域中放置volatileconst对象。 此外,如果从不使用其地址,则实现不需要为这样的对象分配存储

§6.2.5/ 19说:

void类型包含一组空值; 它是一种不完整的类型,无法完成。

§6.3.2.1/ 1说:

左值是具有对象类型或除void之外的不完整类型的表达式; 53) […]

因此, void不是左值,因此类型限定符( constvolatilerestrict )对于void类型的表达式没有意义。 因此,在任何符合C99的编译器中, const voidvolatile void都是没有意义的(尽管指向 const voidconst volatile 指针 有意义的)。

此外,§6.9.1/ 3的约束不允许函数返回合格类型的void

函数的返回类型应为void或数组类型以外的对象类型。

由于这是一个约束,因此符合标准的编译器必须发出诊断(第5.1.1.3 / 1节)。 因此在C99中不允许返回volatile void的函数。

至于volatile void可能曾经做过什么,我不知道也无法真正推测。 我猜你正在寻找的AES代码可能只是旧的,从未被清理过。

参考

证据

  • volatile void function(...)并不严格符合C99。 (感谢@Adam和@ouah。@ Adam深入研究C99规范,以及@ouah将我指向上面列出的DR。)

  • GCC在版本2.5中添加了__attribute__((noreturn))作为volatile void的替代,但是在版本4.6.3之后继续接受volatile void以支持与版本2.5之前的编译器的代码兼容性。 (海湾合作委员会文件。)

  • 上面引用的代码确实将控制权返回到调用它的位置,因为指令似乎不操纵地址寄存器,也不执行跳转命令。 相反,它们将各种值加载到32位寄存器中。 (代码考试。)

  • 第323行到第333行中的命令实现了支持加密操作的特殊操作码。 (代码检查加’挂锁’代码。)

  • 使用汇编函数的代码显然希望它们返回。 (代码考试。)

  • noreturn属性告诉编译器函数没有返回,因此编译器可以根据它进行优化。 (海湾合作委员会文件。)

  • 从GCC文档中:在调用noreturn函数之前,不要假设调用函数保存的寄存器已恢复。

这是与同事的讨论,最终让我陷入困境。当函数声明它不会返回时,编译器必须做一些不同的事情。 对GCC文件的审查证实了这一点。

答:原因

你需要问自己以下问题。

问题: AES代码专门将值加载到32位寄存器中,并对它们执行操作。 它是如何将答案返回到其余代码的?

答案: GCC优化意味着调用函数的寄存器(否则在返回时会覆盖这些值)不会被保存。 汇编语言函数中的计算结果保留在寄存器中,供后续代码使用。

B.现在取得预期的效果:

几乎不管它。 您可能要做的唯一事情是将volatile void返回类型替换为void ,并将noreturn属性添加到函数中。 从理论上讲,这应该具有完全相同的效果。 在实践中,它没有破坏,不修复它。

可取

绝对不鼓励广泛使用这种技术。 首先,它取决于每个编译器的自定义。 其次,它取决于那些没有改变他们如何处理“不归”案件的编制者。 第三,它可能会让后续的维护者感到困惑。

这样的事情唯一有意义的情况是当你利用高度专业化的机器代码时,实现速度上不可能的改进。 即便如此,它应该与权衡取舍平衡。

在此示例中,仅支持两个编译器,并且仅当计算机具有特定的硬件支持才能利用。 否则,它们都通过标准C代码处理。 这是很多努力。 确保它在你做之前付清。