我一直在浏览一些汇编编程视频,以更好地了解如何手动优化编译后留下的 *.s 文件gcc/g++ -S ...
涵盖的主题之一是重构冗余代码,它演示了如何将冗余代码移动到自己的标记块以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 和各种优化(包括 -Os、-O2、-O3、-pipe、-combine 和 -fwhole-program)编译了一些 C 和 C++ 项目,并分析了生成的 *.s使用轻微修补(用于 .s 文件)版本的duplo来实现冗余文件。只有 -fwhole-program (现在已弃用 IIRC)对消除文件之间的重复代码有显着影响(我假设它的替换 -flto 在链接时会表现相似 - 大致相当于使用 -ffunction-sections -fdata-sections 进行编译并与 --gc-sections 链接),但仍然遗漏了大量的代码块。
当仅对具有至少 5 个连续重复指令的连续程序块进行重复数据删除时,使用 duplo 输出进行手动优化可在随机 C 项目中减少约 10% 的大小,在随机 C++ 项目中减少近 30%。
我是否缺少一个编译器选项(甚至是一个独立工具),该选项在编译大小时会自动消除冗余程序集(包括其他编译器:clang、icc 等) ,还是缺少此功能(出于某种原因?)?
如果它不存在,则可以修改duplo以忽略以“。”开头的行。或者 ';' (和其他人?)并用重复代码的函数调用替换重复的代码块,但我愿意接受其他直接与编译器的内部表示(最好是clang或gcc)一起工作的建议。
编辑:我修补了 duplo 以在此处识别重复程序集的块,但目前仍需要手动重构。只要使用相同的编译器生成代码,就有可能(但可能很慢)识别最大的重复代码块,将它们放在自己的“功能”块中,并用对该块的 CALL 替换代码.