如何解决GCC是否优化程序集源文件?
我可以使用GCC将汇编代码文件转换为可重新分配的文件。
gcc -c source.S -o object.o -O2
优化选项是否有效?我可以期望GCC优化我的汇编代码吗?
解决方法
否。
GCC将您的汇编源传递给预处理器,然后传递给汇编器。任何时候都不会进行任何优化。
,如果您不想手动优化汇编语言,那么汇编语言是您选择源语言的错误选择。如果您想要类似asm的东西,但实际上是优化编译器的输入,也许可以考虑使用LLVM-IR。 (并且独立于ISA。)
公平地说,有 一些二进制到二进制的重新编译器/优化器,它们试图弄清楚什么是实现细节,什么是重要逻辑,并据此进行优化。 (也可以从asm源而不是机器代码中进行读取; asm和机器代码易于来回转换,并且具有接近1:1的映射)。但这不是汇编程序的工作。
汇编程序的工作通常只是忠实地将您编写的内容转换为asm。有一个工具可以做到这一点,这对于尝试更快地找出真正的 很有必要,而无需手工编写实际的机器代码。
有趣的是,对于GAS来说,GNU汇编器 具有一些针对x86的有限优化选项,即使您运行{{1 }}。 (您可以运行gcc -O2
来查看前端如何调用其他程序来完成实际工作,以及使用哪些选项。)
使用gcc -v ...
可以对C进行全面优化,并针对ASM进行GAS的次要窥孔优化。(或者gcc -Wa,-Os -O3 foo.c bar.S
,不幸的是手册是错误的,{{1 }}错过了-Wa,-O2
的一些优化,-Os
在-O2
命令行中传递了-Wa,...
,就像...
通过GCC前面板传递链接器选项一样,结束。
GCC通常不会启用as
的优化,因为它通常会提供GAS已经优化的asm。
GAS的优化仅适用于单独的单个指令,因此,仅当一条指令可以被具有完全相同架构效果的另一条指令替换(长度,因此对RIP的影响有所不同)。 micro -建筑效果(性能)也可以不同。这就是非尺寸优化的重点。
在as(1)
man page中,请注意,这些是-Wl,...
选项,不是 as
选项。
as
以较小的指令大小优化指令编码。
gcc
和-O0 | -O | -O1 | -O2 | -Os
用64位编码64位寄存器加载指令 立即作为具有31位或32位的32位寄存器加载指令 32位立即数,对64位寄存器清除指令进行编码
具有32位寄存器清除指令,编码256位/ 512位 带128位VEX的VEX / EVEX向量寄存器清除指令 向量寄存器清除指令,对128位/ 256位EVEX进行编码 向量寄存器,带有VEX向量寄存器的加载/存储指令 加载/存储指令,并编码打包的128位/ 256位EVEX 打包128位/ 256位VEX的整数逻辑指令 整数逻辑。
-O
包括-O1
优化,并对256位/ 512位EVEX进行编码 具有128位EVEX向量的向量寄存器清除指令 注册清算说明。在64位模式下VEX编码 带有可交换源操作数的指令也将具有 如果允许使用2字节VEX前缀,则交换源操作数 形式,而不是3字节的形式。某些形式的AND和OR 两次指定相同的(寄存器)操作数也将是 更改为“测试”。
-O2
包括-O1
优化,并编码16位,32位和
64位寄存器测试,立即变为8位寄存器测试,
即时。-Os
关闭此优化。
(re:一些VEX / EVEX操作数大小和代码大小优化:Is vxorps-zeroing on AMD Jaguar/Bulldozer/Zen faster with xmm registers than ymm?以及我对Instruction Lengths的回答接近结尾的部分re:2 vs. 3字节VEX前缀)
很遗憾,-O2
和-O0
发生了冲突,-O2
实际上并未包含-Os
中的所有内容。您无法获得将-Os
优化为-O2
(test [re]dx,1
)和将test dl,1
优化为-Os
({{ }}。
or al,al
不可编码,因此edx版本需要一个imm32。test al,al
是not useful for x86的一种过时的8080习语,除了有时在P6家族中,是为了避免寄存器读取停顿,而故意避免重写寄存器实际上是更好的,而不是避免加长寄存器。深度链。
-O2
与test r/m32,imm8
组装在一起(出于某些疯狂的原因,or al,al
对现代AMD CPU具有.intel_syntax noprefix
shufps xmm0,xmm0,0
vxorps zmm31,zmm31,zmm31
vxorps zmm1,zmm1,zmm1
vxorps ymm15,ymm15,ymm15
vpxord zmm15,zmm15,zmm15
vpxord ymm3,ymm14,ymm15
vpxord ymm3,ymm4,ymm15
vmovd xmm16,[rdi + 256] # can use EVEX scaled disp8
vmovd xmm0,[rdi + 256] # could use EVEX scaled disp8 but doesn't even with a -march enabling AVX512
xor rax,rax
or al,al
cmp dl,0
test rdx,1
mov rax,1
mov rax,-1
mov rax,0xffffffff80000000
.att_syntax
movabs $-1,%rax
movq $1,%rax
movabs $1,%rax
支持,但对Intel最高不超过gcc -g -Wa,-msse2avx -Wa,-O2 -Wa,-march=znver2+avx512dq+avx512vl -c foo.s
和某些Xeon Phi, Skylake-avx512就像GCC一样。所以我必须手动启用AVX512进行测试。
as
源+反汇编
-march=
因此,不幸的是,即使没有必要使用EVEX,即使显式启用AVX512VL和AVX512DQ也无法使GAS为corei7
选择较短的EVEX编码。也许这仍然是故意的:您可能希望一些函数使用AVX512,而有些则可以避免。如果使用ISA选项限制来捕获对ISA扩展的意外使用,则必须为整个此类文件启用AVX512。意外地找到使用EVEX的汇编器,可能会令人惊讶。
您可以使用objdump -dwrC -Mintel -S
手动强制它。 (不幸的是,在编译C时,GCC并没有执行,0000000000000000 <.text>:
.intel_syntax noprefix
shufps xmm0,0 # -msse2avx just for fun
0: c5 f8 c6 c0 00 vshufps xmm0,0x0
vxorps zmm31,zmm31 # avoids triggering AVX512 frequency limit
5: 62 01 04 00 57 ff vxorps xmm31,xmm31,xmm31
vxorps zmm1,zmm1 # shorter,using VEX
b: c5 f0 57 c9 vxorps xmm1,xmm1,xmm1
vxorps ymm15,ymm15 # missed optimization,could vxorps xmm15,xmm0 for a 2-byte VEX and still be a zeroing idiom
f: c4 41 00 57 ff vxorps xmm15,xmm15,xmm15
vpxord zmm15,zmm15 # AVX512 mnemonic optimized to AVX1,same missed opt for source operands.
14: c4 41 01 ef ff vpxor xmm15,xmm15
vpxord ymm3,ymm15 # no optimization possible
19: c4 c1 0d ef df vpxor ymm3,ymm15 # reversed operands to allow 2-byte VEX
1e: c5 85 ef dc vpxor ymm3,ymm4
vmovd xmm16,[rdi + 256] # uses EVEX scaled disp8 because xmm16 requires EVEX anyway
22: 62 e1 7d 08 6e 47 40 vmovd xmm16,DWORD PTR [rdi+0x100]
vmovd xmm0,[rdi + 256] # could use EVEX scaled disp8 but doesn't even with a -march enabling AVX512
29: c5 f9 6e 87 00 01 00 00 vmovd xmm0,DWORD PTR [rdi+0x100]
xor rax,rax # dropped REX prefix
31: 31 c0 xor eax,eax
or al,al
33: 84 c0 test al,0 # optimization to test dl,dl not quite legal: different effect on AF
35: 80 fa 00 cmp dl,0x0
test rdx,1 # partial optimization: only to 32-bit,not 8-bit
38: f7 c2 01 00 00 00 test edx,0x1
mov rax,1
3e: b8 01 00 00 00 mov eax,0x1
mov rax,-1 # sign-extended imm8 required
43: 48 c7 c0 ff ff ff ff mov rax,0xffffffffffffffff
mov rax,0xffffffff80000000
4a: 48 c7 c0 00 00 00 80 mov rax,%rax # movabs forces imm64,despite -O2
51: 48 b8 ff ff ff ff ff ff ff ff movabs rax,0xffffffffffffffff
movq $1,%rax # but explicit q operand size doesn't stop opt
5b: b8 01 00 00 00 mov eax,0x1
movabs $1,%rax
60: 48 b8 01 00 00 00 00 00 00 00 movabs rax,0x1
肯定是做可以自由支配在任何地方使用AVX512指令。)
so.s
#define HELLO 0x5
mov $HELLO,%eax
mov $0x5,%eax
retq
gcc -O2 -c so.s -o so.o
objdump -d so.o
0000000000000000 <.text>:
0: b8 00 00 00 00 mov $0x0,%eax
5: b8 05 00 00 00 mov $0x5,%eax
a: b8 05 00 00 00 mov $0x5,%eax
f: b8 05 00 00 00 mov $0x5,%eax
14: c3 retq
它甚至没有预处理定义。
将so.s重命名为so
gcc -O2 -c so.S -o so.o
objdump -d so.o
0000000000000000 <.text>:
0: b8 05 00 00 00 mov $0x5,%eax
14: c3 retq
它对定义进行预处理,但是没有进行优化。
更深入地了解以及传递给的内容
gcc -O2 -c -save-temps so.s -o so.o
[0][as]
[1][--64]
[2][-o]
[3][so.o]
[4][so.s]
cat so.s
#define HELLO 0x5
mov $HELLO,%eax
retq
和
gcc -O2 -c -save-temps so.S -o so.o
[0][as]
[1][--64]
[2][-o]
[3][so.o]
[4][so.s]
cat so.s
# 1 "so.S"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "so.S"
mov $0x5,%eax
retq
仍然没有优化。
应该足以证明。您可以执行一些链接时间优化,以正确构建对象,然后告知链接器。但是我怀疑它不是在机器代码级别上执行,而是在更高级别上重新生成代码。
int main ( void )
{
return(5);
}
gcc -O2 so.c -save-temps -o so.o
cat so.s
.file "so.c"
.section .text.unlikely,"ax",@progbits
.LCOLDB0:
.section .text.startup,@progbits
.LHOTB0:
.p2align 4,15
.globl main
.type main,@function
main:
.LFB0:
.cfi_startproc
movl $5,%eax
ret
.cfi_endproc
.LFE0:
.size main,.-main
.section .text.unlikely
.LCOLDE0:
.section .text.startup
.LHOTE0:
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"
.section .note.GNU-stack,"",@progbits
从上方使用so.S
gcc -flto -O2 so.S -save-temps -o so.o
cat so.s
# 1 "so.S"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "so.S"
mov $0x5,%eax
retq
从上方使用so.c
gcc -flto -O2 so.c -save-temps -o so.o
cat so.s
.file "so.c"
.section .gnu.lto_.profile.3f5dbe2a70110b8,"e",@progbits
.string "x\234ca`d`a`"
.string "\222L\214"
.string ""
.string "o"
.ascii "\016"
.text
.section .gnu.lto_.icf.3f5dbe2a70110b8,@progbits
.string "x\234ca`d"
.string "\001\016\006\004`d\330|\356\347Nv\006"
.ascii "\017\243\003I"
.text
.section .gnu.lto_.jmpfuncs.3f5dbe2a70110b8,@progbits
.string "x\234ca`d"
.string "\001V\006\004"
.string "\213"
.string ""
.string ""
.string "\356"
.ascii "\f"
.text
.section .gnu.lto_.inline.3f5dbe2a70110b8,@progbits
.string "x\234ca`d"
.string "\001\021\006\004"
.string "\21203120\001\231l\013\344\231\300b"
.string "\n\031"
.ascii "\352"
.text
.section .gnu.lto_.pureconst.3f5dbe2a70110b8,@progbits
.string "x\234ca`d`f`"
.string "\222\f"
.string ""
.string "X"
.ascii "\n"
.text
.section .gnu.lto_main.3f5dbe2a70110b8,@progbits
.ascii "x\234\035\216\273\016\001a\020\205\347\314\277\313\026\210\236"
.ascii "B\253\3610^\301\003(<\300\376\330B\024\262\005\211\210r\223-"
.ascii "\334[\3256\n\005\2117\020\n\211NH(\0043&9\2319\231o.\016\201"
.ascii "4f\242\264\250 \202!p\270'jz\fha=\220\317\360\361bkp\b\226c\363"
.ascii "\344\216`\216\330\333nt\316\251\005Jb/Qo\210rl%\216\233\276\327"
.ascii "\r\3211L-\201\247(b\202\242^\230\241L\302\236V\237A6\025([RD"
.ascii ":s\244\364\243E5\261\337o\333&q\336e\242\273H\037y0k6W\264\362"
.ascii "\272`\033\255\337\031\275\315p\261\370\357\026\026\312\310\204"
.ascii "\333\250Wj\364\003\t\210<\r"
.text
.section .gnu.lto_.symbol_nodes.3f5dbe2a70110b8,@progbits
.string "x\234ca`d\020f"
.string "\002&\206z\006\206\t\347\030@\324\256\206@\240\b"
.ascii "'\370\004\002"
.text
.section .gnu.lto_.refs.3f5dbe2a70110b8,@progbits
.string "x\234ca`\004B "
.string ""
.string ""
.string "9"
.ascii "\007"
.text
.section .gnu.lto_.decls.3f5dbe2a70110b8,@progbits
.string "x\234\205PMK\002Q\024\275\347\315h\222\021R-\\\270\020\027\355\222\244\020\367A\355b6A\264\013\261p\221AmZ^\377\200DB\340N\004)\320j~A\bA\021\371\007J!\241e\277@\b\354\276y3\216\320\242\013\367\343\335w\3369\367]\233@\332\372\222V%\357\213O\304\224\344\003\nM\243\\\372k\272g\211/\211\257\210;\377\340\331\302w{\370\025\031\340\035\242\201D\202\022\004xC\350\344\225\306\275\243\024\312\213\024\266\020"
.ascii "\375\263\nJ_\332\300u\317\344I`\001\211O\345\253i\006\302tB\363"
.ascii "\b\360X\303\247Se\005\337h\226\330\260\316\360\032q\177\023A"
.ascii "\224\337\337<\266\027\207\370\2502s\223\331\301T\322[#Q\224\331"
.ascii "\326\373\204\2058\321\302S\203\235+\301\266\270\247\367%\004"
.ascii "\215\376[\335\262\226\241\353\317\361\355v\266+\327|\311\254"
.ascii "\n\341\216;?\265\227x\362Z\337\214\252\234\006\234yl\244\260"
.ascii "\236\022\261\007$%\036\331\0069~\346V4\323d\327\345Q\375U\325"
.ascii "\270\247GS\032\205;\031\342\036Y=\241\224\022\273\030\002\035"
.ascii "\fd`\027\031\232\273(\344\327\362\233\024;.UJg\345\"\331'\207"
.ascii "\345Jlgw/\275\225\313Q\344\3744[\244_\320\267k~"
.text
.section .gnu.lto_.symtab.3f5dbe2a70110b8,@progbits
.string "main"
.string ""
.string ""
.string ""
.string ""
.string ""
.string ""
.string ""
.string ""
.string ""
.string ""
.string ""
.string "\260"
.string ""
.string ""
.text
.section .gnu.lto_.opts,@progbits
.string "'-fmath-errno' '-fsigned-zeros' '-ftrapping-math' '-fno-trapv' '-fno-openmp' '-fno-openacc' '-mtune=generic' '-march=x86-64' '-O2' '-flto' '-fstack-protector-strong'"
.text
.comm __gnu_lto_v1,1,1
.comm __gnu_lto_slim,1
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"
.section .note.GNU-stack,@progbits
因此,gcc似乎仍未进行任何优化,以删除这些重复的指令,这些指令没有功能优势,并且基本上是无效代码。它确实表明如果文件具有.S,则gcc将对其进行预处理,而如果.s没有,gcc将对其进行预处理(可以尝试或阅读其他.asm上的文档吗?)。这些文件在Linux上运行,gcc是gcc,binutils是binutils,特定文件名的扩展名敏感性可能因目标主机而异。
链接时间的优化似乎与高级代码有关,因为人们不希望汇编语言代码。人们期望链接时间优化基于中端代码而不是后端。
我们知道gcc不是汇编程序,即使它是从C生成的,它也只是将其传递给它,因此它需要一个汇编器解析器,然后需要逻辑来处理该语言,然后挑选出要传递的东西进行链接时间优化。
您可以阅读有关链接时间优化的更多信息,并查看是否有一种方法可以将其应用于汇编程序...我想没有,但是您的整个问题都与如何使用工具以及它们如何工作有关。
汇编语言优化并不一定是一回事,这很重要,现在对于伪指令有一些伪代码,汇编器可以选择优化的实现
ldr r0,=0x12345678
ldr r0,=0x1000
ldr r0,=0xFFFFFF12
00000000 <.text>:
0: e59f0004 ldr r0,[pc,#4] ; c <.text+0xc>
4: e3a00a01 mov r0,#4096 ; 0x1000
8: e3e000ed mvn r0,#237 ; 0xed
c: 12345678 .word 0x12345678
但这是伪代码,因此支持它的汇编器可以自由地执行他们想要的任何事情。 (汇编程序定义汇编语言(而不是目标语言),因此根据定义,他们可以执行自己想要的任何事情)。关于这一点,当工具链中也有汇编器时,使用编译器作为汇编器,因为该工具定义了汇编语言,因此将其更改为另一种汇编语言。因此,当您允许gcc预处理代码时,您基本上使用的是与as不同的汇编语言。就像编译器的内联汇编一样,是另一种汇编语言。对于gnu工具链,每个目标至少需要三种汇编语言。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。