【问题标题】:Automatic code deduplication of assembly language?汇编语言的自动代码去重?
【发布时间】:2013-10-29 16:20:40
【问题描述】:

我已经阅读了一些Assembly Programming Videos 以更好地了解如何手动优化使用gcc/g++ -S ... 编译后留下的*.s 文件其中一个主题是Refactoring Redundant Code,它演示了如何移动冗余代码到它自己的以ret结尾的标记块,并将其替换为call

视频中给出的示例是 2 个块,其中包含:

mov eax,power
mul ebx
mov power,eax
inc count

它用call CalculateNextPower 替换,CalculateNextPower 看起来像:

CalculateNextPower:
mov eax,power
mul ebx
mov power,eax
inc count
ret

出于好奇,为了减小编译大小,我编译了一些 C 和 C++ 项目,使用 -S 和各种优化,包括 -Os、-O2、-O3、-pipe、-combine 和 -fwhole-program,并分析了结果*.s 文件用于冗余使用 duplo 的轻微修补(用于 .s 文件)版本。只有 -fwhole-program (现在已弃用 IIRC) 对消除文件之间的重复代码有显着影响(我假设它的替换 -flto 在链接时会表现类似 - 大致相当于使用 -ffunction-sections -fdata-sections 编译并使用 --gc-sections 链接) 但仍然会遗漏大量代码。

使用 duplo 输出进行手动优化可在随机 C 项目中减少约 10% 的大小,在仅对具有至少 5 个连续重复指令的连续程序块进行重复数据删除时,在随机 C++ 项目中减少近 30%。

我是否缺少一个编译器选项(甚至是一个独立工具),该选项在针对大小进行编译时会自动消除冗余程序集(包括其他编译器:clang、icc 等。)缺少此功能(出于某种原因?)?

如果它不存在,可以修改duplo 以忽略以'.' 开头的行。或者 ';' (和其他人?) 并用重复代码的函数调用替换重复的代码块,但我愿意接受其他直接与编译器的内部表示(最好是 clang 或 gcc)一起工作的建议。

编辑:我修补了 duplo 以识别重复程序集块 here,但目前仍需要手动重构。只要使用相同的编译器生成代码,就有可能(但可能很慢)识别最大的重复代码块,将它们放在自己的“功能”块中,并用对该块的 CALL 替换代码.

【问题讨论】:

  • 这反映了我从嵌入式编程中学到的东西:编译器很愚蠢;开发人员很聪明。因此,当真正需要(小于 8 位内存)时,我总是使用可能会严重影响代码可维护性的优化。在您的情况下,您可以尝试查看 --param 值。对于其中一些,它们是在值非常大的情况下对内存或时间使用的警告。第一个要设置的是--param lto-partitions=1,因此您将整个源代码编译为单个c 文件(而不是32)
  • 我想知道如何恢复 -pipe -combine -fwhole-program 的全部功能。谢谢@user2284570。
  • 我阅读了整个手册页以继续,你应该这样做...结果是每次我启动 gcc 时,打印的命令占了我完整的一半高清屏幕由于编译器标志和优化的数量。不要忘记使用-march=native -mtune=native。但不要忘记规则:单个人为优化通常比许多编译器更有效。在一些嵌入式环境中,程序由goto 和labels 组成。这就是为什么代码的可维护性/可读性通常与代码速度和内存使用相对立。

标签: assembly size compiler-optimization code-duplication


【解决方案1】:

你想要的是clone detector tool

这些存在于各种实现中,具体取决于正在处理的文档元素的粒度以及可用结构的多少。

那些匹配原始行的(对你不起作用,你想通过不同的常量[数据和索引偏移]和/或命名位置或其他命名子程序来参数化你的子程序)。基于令牌的检测器可能会起作用,因为它们将识别变化的单点位置(例如,常量或标识符)。但你真正想要的是一个结构匹配器,它可以挑选出变体寻址模式,甚至是块中间代码中的变体(参见我碰巧构建的基于 AST 的克隆检测器)。

要检测结构,你必须结构。幸运的是,即使是汇编语言代码也具有语法形式的结构,以及由子例程入口和出口分隔的代码块(后者在汇编中检测起来有点问题,因为每个代码块可能不止一个)。

当您检测到使用结构时,您至少有可能使用该结构来修改代码。但是,如果您将程序源表示为一棵树,则您具有可以检测克隆的结构(子树和子树序列),并且可以通过修改匹配点处的树来抽象克隆匹配。 (我的 COBOL 克隆检测器的早期版本将克隆抽象到 COPY 库中。我们停止这样做主要是因为您不想以那种方式抽象每个克隆)。

【讨论】:

  • +1 表示“克隆检测器”提示。它没有帮助我找到一个完整的解决方案,但可能是一个很好的起点。
  • 在我的简历中查看语言参数化的结构克隆检测器,它可以很容易地通过汇编程序描述进行参数化。克隆检测器背后的机器可以完成抽象步骤。
【解决方案2】:

您提出的建议称为程序抽象,并且已被多个小组作为研究项目实施。 Here is one.Here's another.And another.

克隆检测通常用于源代码的上下文中,尽管其功能相似。由于过程抽象发生在较低级别,它可以完成更多。例如,假设有两个对不同函数的调用,但具有完全相同的复杂参数计算。过程抽象器可以将参数计算拉入过程,但克隆检测器很难做到这一点。

我认为 gcc 或 llvm 目前都不支持 PA 的实现。我搜索了两组文件,但没有找到。在上述至少两种情况下,优化器在 gcc 生成的汇编代码上运行,而不是作为 gcc 内部优化。这可能解释了为什么这些技术没有内置到编译器中。你可以试试作者,看看他们的实现在哪里。

【讨论】:

  • 好的克隆检测器不限于语句。建立了克隆检测器,尤其是。使用语法处理结构相似性检测的那种,我可以告诉你,如果有两组(非常相似的)表达式用于计算参数,克隆检测器可以检测到这些。它是否报告它们(可能非常小)是一个告诉报告小型克隆的阈值是多少的问题。你所说的“PA”曾经被称为“通用子表达式消除”,这就是我的特定克隆检测器的灵感。
猜你喜欢
  • 1970-01-01
  • 2014-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-15
相关资源
最近更新 更多