原文
假设有个想要将一个32位值传递给一个带64位值的函数的函数.你不关心高32位的内容,因为该值是传递给回调函数的直通值,回调函数会把它截断为32位值.
因此,你都担心编译器一般生成的将32位值扩展到64位值的那条指令的性能影响.
我怀疑这条指令不是程序中的性能瓶颈.
我想出的是说:可不执行任何指令从32位值生成64位值"的gcc/clang内联汇编.
int64_t int32_to_64_garbage(int32_t i32)
{int64_t i64;__asm__("" ://闲着"=r"(i64) ://在`寄存器`中生成结果"0"(i32));//从此最后的输入return i64;
}
__asm__内联指令的第一个参数是要生成的代码.传递一个空串,所以实际上未生成任何代码!想要的所有效果都在输入和输出的声明中.
接着是只有一个的输出."=r"(i64)表示内联汇编会在编译器选择的r寄存器中,放入i64的覆盖(=)值,内联汇编器按%0引用的.输出从0开始编号.
最后,有这里只有一个的输入."0"(i32)表示输入应在输出的0数字位置放置.
所有工作都是根据输入和输出的约束来完成的.没有实际的代码.告诉编译器,在一个寄存器中放入i32,然后遮住眼睛,睁开时,在同一个寄存器中变成i64!
在3级优化中运行gcc,显示完全省略了该值.
void somewhere(int64_t);
void sample1(int32_t v)
{somewhere(v);
}
void sample2(int32_t v)
{somewhere(int32_to_64_garbage(v));
}
结果是:
//x86-64
sample1(int):movsx rdi, edijmp somewhere(long)
sample2(int):jmp somewhere(long)//ARM32
sample1(int):asrs r1, r0, #31b somewhere(long long)
sample2(int):b somewhere(long long)//ARM64
sample1(int):sxtw x0, w0b somewhere(long)
sample2(int):b somewhere(long)
第一个版本在尾调用之前,包含显式符号扩展指令.第二个版本是直接尾调用,在rdi,寄存器高32位中使用任意垃圾.
另一个支持gcc扩展内联语法的编译器是icc,该技巧似乎也有效.
//x86-64
sample1(int):movsxd rdi, edijmp somewhere(long)
sample2(int):jmp somewhere(long)
clang``编译器还支持gcc扩展内联汇编语法.但是,它不仅会生成转换,而且还会丢失尾调用.
//x86-64
sample1(int):movsxd edi, edijmp somewhere(long)@PLT
sample2(int):push raxmov edi, edicall somewhere(long)@PLTpop raxret//ARM32
sample1(int):asr r1, r0, #31b somewhere(long long)
sample2(int):push {r11, lr}sub sp, sp, #8mov r1, #0bl somewhere(long long)add sp, sp, #8pop {r11, pc}//ARM64
sample1(int):sxtw x0, w0b somewhere(long)
sample2(int):sub sp, sp, #32stp x29, x30, [sp, #16]add x29, sp, #16mov w0, w0bl somewhere(long)ldp x29, x30, [sp, #16]add sp, sp, #32ret
更新:似乎当前版本的clang(当前时)恢复了尾调用,尽管它仍执行32到64的正转换,因此成本基本相同.
//x86-64
sample1(int):movsxd edi, edijmp somewhere(long)@PLT
sample2(int):mov edi, edijmp somewhere(long)@PLT//ARM32
sample1(int):asr r1, r0, #31b somewhere(long long)
sample2(int):mov r1, #0b somewhere(long long)//ARM64
sample1(int):sxtw x0, w0b somewhere(long)
sample2(int):mov w0, w0b somewhere(long)
VC++``编译器不支持gcc扩展内联语法,因此无法检查.
因为msvc完全不管用,并且对clang没有任何好处,因此我只会在使用gcc或icc``编译时允许此优化,并在其他地方使用额外指令.