汇编语言的自动代码重复数据删除?

时间:2013-10-29 16:20:41

标签: assembly size compiler-optimization code-duplication

我一直在浏览一些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

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

使用duplo输出的手动优化导致随机C项目的大小减少约10%,而随机C ++项目中只有30%,只有连续的至少5个连续重复指令的连续块被重复数据删除。

我是否遗漏了一个编译器选项(甚至是一个独立的工具),在编译大小(包括其他编译器:clang,icc等等)时自动消除冗余程序集或者是这个功能缺席(有原因吗?)?

如果它不存在,则可以修改duplo以忽略以'。'开头的行。要么 ';' (以及其他?)并使用重复代码调用函数替换重复的代码块,但我愿意接受其他可直接使用编译器内部表示的建议(最好是clang或gcc)。 / p>

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

2 个答案:

答案 0 :(得分:2)

你想要的是clone detector tool

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

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

要检测结构,您必须拥有结构。幸运的是,即使汇编语言代码也有语法形式的结构,以及由子例程条目和出口分隔的代码块(后者在汇编时检测更有问题,因为每个代码可能不止一个)。 p>

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

答案 1 :(得分:0)

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

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

我不相信gcc或llvm当前都支持PA的实现。我搜索了两组文件但没找到。在上面的至少两种情况中,优化器运行在由gcc生成的汇编代码而不是gcc内部优化。这可能解释了为什么这些技术没有内置到编译器中。您可以尝试作者查看其实现的位置。