【问题标题】:Optimizer: optimize inline assembly优化器:优化内联汇编
【发布时间】:2016-11-13 19:39:25
【问题描述】:

我听说过几次编译器不会优化内联汇编,或者内联汇编对它来说是一个黑盒。我很怀疑,因为我没有看到任何编译器失败的情况,所以我不在乎。

但是今天我在 GCC wiki 上发现了一个名为 DontUseInlineAsm 的页面。 它包含人们之前告诉我的相同问题,但没有详细说明为什么编译器无法理解内联汇编,因此不会对其进行优化。那么,有谁知道编译器不进行这些优化的原因?

当然,我会把特殊情况放在一边

asm volatile("" : : "g"(value) : "memory");
asm volatile("" : : : "memory");

当我们明确告诉编译器这段代码有可见的副作用,因此不应该优化它时。

【问题讨论】:

  • 优化代码本身和“优化掉”(即删除)是两件不同的事情,不确定你问的是哪一个。
  • @jester,是的,我明白其中的区别。只是想指出,我理解在某些情况下编译器无法使用内联汇编做任何事情
  • 典型的过程是将编译器设置为最大优化并编译函数。你打印出函数的汇编语言列表。使用它作为您的汇编代码的基础;优化编译器生成的程序集。

标签: c++ assembly optimization


【解决方案1】:

您的问题似乎基于错误的假设,即编译器首先生成程序集,然后,如果您想要优化输出,那么它会读取它生成的程序集,对其进行优化,然后将其写回。如果是这样的话,那么阅读和优化你的内联汇编应该没什么大不了的,对吧?

编译器不会优化您的内联程序集,因为编译器根本不会优化任何程序集。编译器无法在所需的级别上理解汇编,以便对其进行优化。这不关它的事。

编译器通过对其内部数据结构(解析树、p 代码等中间语言等)进行特殊技巧来生成优化的机器代码,这些数据结构

如果涉及程序集生成步骤,则它是只写步骤,这意味着编译器将为您生成此程序集,但绝不会尝试读取它。这就是装配工的工作。而且我从未听说过优化汇编器。

因此,可以假设没有编译器会尝试优化任何人的内联汇编。

我不了解你,但坦率地说,如果编译器试图修改我的内联程序集,我会非常恼火。如果我要使用汇编,我会这样做,因为我比编译器更了解(或者我认为我知道)。

【讨论】:

  • 让我们以rotl(左循环移位)指令为例。在 GCC 中没有内置函数,因为我不想时不时地编写内联汇编,所以我创建了一个函数 CyclicShiftLeft(x, n)。然后我写CyclicShiftLeft(CyclicShiftLeft(x, n), k)(也许这是内联的结果)我希望它会被优化成CyclicShiftLeft(x, n + k)。我认为编译器应该能够进行这样的优化(我没有检查),但要做到这一点,编译器应该能够优化内联汇编。
  • 嗯,是的,除了你不能真正规定编译器应该能做什么,除非你是一家生产编译器的公司的开发主管。编译器永远不会优化内联汇编,因此您永远不会实现示例所需的这种相当简单的优化。另一方面,如果 GCC 足够聪明,那么它可能能够检测到通过移位和掩码模拟 ROTL 的 C 指令序列实际上正在尝试执行 ROTL,并将它们替换为实际的 ROTL 指令。你试过吗?
  • 对,所以我预测正确。我现在对此有一种舒服的感觉。 C-:= 好吧,正如我所说,编译器不会做诸如“用它的内部字节码替换程序集”之类的事情。将汇编转换为除机器代码之外的任何东西是一项极其艰巨的任务,我怀疑是否有任何工具可以在这方面取得成功。所以,如果你觉得自己能胜任,那就让自己获得博士学位。而在你面前只有名誉和荣耀。
  • @KostyaBazhanov:迈克是对的。编译器将 C 或 C++ 转换为代码功能的内部表示,并对其进行优化。二进制到二进制优化是一项艰巨的任务,因为可用的信息要少得多。例如在 C 中,编译器知道函数末尾的临时寄存器中的值无关紧要,只有返回值。在修改函数代码时,asm to asm 优化器必须确定在 EDX 中保留不同的值是否可行。
  • @KostyaBazhanov:存在二进制到二进制优化器,但很少使用(而且 gcc 不是这样工作的)。 This question 就是这样(但答案并不是我真正想要的。我还没有找到任何关于二进制重新编译的具体提及。IIRC,@Ira Baxter 已经发布了这个,甚至开发了这样的工具) .基本上,您对编译器如何工作的心智模型是错误的,这就是为什么您认为它们很容易优化内联汇编。所以你的问题是有道理的,但这就是答案。
【解决方案2】:

编译器不会优化内联汇编,因为那会破坏目的。当程序员认为他们比编译器更了解时,使用内联汇编,或者程序员认为他们可以生成更好的代码,或者编译器无法生成他们想要的代码。在前一种情况下,程序员自己优化汇编代码,如果编译器在优化等效的 C 代码方面做得不够好,那么它就不可能改进汇编代码。在后一种情况下,没有等效的 C 代码,内联汇编正在使用编译器无法生成的指令或其他汇编功能。在这种情况下,它也不太可能理解这些指令实际上做了什么来优化代码。

没有编译器能够将内联汇编转换为其内部“字节码”,正如您在评论中所建议的那样。 GCC 将内联汇编视为字符串以粘贴到它的汇编输出中。它完全不了解字符串中的代码。 Clang 通常不会生成汇编作为输出,因此它有一个内置的汇编器,但它也并不真正理解汇编代码。它只是将其翻译成机器代码,然后插入到目标文件输出中。微软的编译器是另一种通常不会生成汇编代码输出的编译器,它实际上对汇编有所了解,但只是程度有限。它只了解代码使用的寄存器之类的东西,因此编译器会执行诸如保留内联汇编使用的寄存器之类的事情。它不知道汇编代码实际上做了什么。

如果您希望编译器优化您的代码,请不要使用内联汇编。即使不是直接对应于您想要的汇编代码的语言功能,编译器也可能无论如何都能够生成它,就像 Mike Nakis 在关于 ROTL 的评论中建议的那样。您还可以使用内在函数、扩展语言并对应各种汇编指令的函数,编译器能够在许多情况下对其进行优化。

【讨论】:

    猜你喜欢
    • 2012-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-11
    • 1970-01-01
    • 1970-01-01
    • 2011-02-14
    • 2016-12-09
    相关资源
    最近更新 更多