原文
假设有个想要将一个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``编译时
允许此优化,并在其他地方
使用额外指令
.