123*_*eee 4 c gcc clang micro-optimization
免责声明:以下是纯粹的学术问题; 我将此代码与任何生产系统保持至少100米的距离.这里提出的问题是任何"现实生活"案件都无法衡量的问题.
考虑以下代码(godbolt链接):
#include <stdlib.h>
typedef int (*func_t)(int *ptr); // functions must conform to this interface
extern int uses_the_ptr(int *ptr);
extern int doesnt_use_the_ptr(int *ptr);
int foo() {
// actual selection is complex, there are multiple functions,
// but I know `func` will point to a function that doesn't use the argument
func_t func = doesnt_use_the_ptr;
int *unused_ptr_arg = NULL; // I pay a zeroing (e.g. `xor reg reg`) in every compiler
int *unused_ptr_arg; // UB, gcc zeroes (thanks for saving me from myself, gcc), clang doesn't
int *unused_ptr_arg __attribute__((__unused__)); // Neither zeroing, nor UB, this is what I want
return (*func)(unused_ptr_arg);
}
Run Code Online (Sandbox Code Playgroud)
编译器没有合理的方法知道unused_ptr_arg不需要(因此归零是浪费时间),但我这样做,所以我想通知编译器unused_ptr_arg可能有任何值,例如寄存器中发生的任何事情都是用于传递给func.
有没有办法做到这一点?我知道我已经超出了标准,所以我可以使用特定于编译器的扩展(特别是对于gcc和clang).
在GCC和Clang以及其他支持GCC扩展汇编语法的编译器中,您可以这样做:
int *unused_ptr_arg;
__asm__("" : "=x" (unused_ptr_arg));
return (*func)(unused_ptr_arg);
Run Code Online (Sandbox Code Playgroud)
该__asm__结构说 "这里有一些汇编代码插入到程序中.它将结果写入unused_ptr_arg您为其选择的任何位置."(x约束意味着编译器可以选择内存,处理器寄存器或机器支持的任何其他内容.)但实际的汇编代码为空("").因此,不会生成汇编代码,但编译器认为unused_ptr_arg已初始化.在用于x86-64的Clang 6.0.0和GCC 7.3(目前在Compiler Explorer中的最新版本)中,这将生成一个jmpno xor.
考虑一下:
int *unused_ptr_arg;
(void) &unused_ptr_arg;
return (*func)(unused_ptr_arg);
Run Code Online (Sandbox Code Playgroud)
即使没有使用(void) &unused_ptr_arg;地址,目的也是取地址unused_ptr_arg.这会禁用C 2011 [N1570] 6.3.2.1中的规则,该规则表示如果程序使用可能已声明的自动存储持续时间的未初始化对象的值,则行为未定义register.因为它的地址被采用,所以它不能被声明register,因此根据此规则使用该值不再是未定义的行为.
因此,该对象具有不确定的值.然后存在指针是否可能具有陷阱表示的问题.如果指针在正在使用的C实现中没有陷阱表示,则由于仅仅引用该值而不会发生陷阱,如将其作为参数传递时.
在Compiler Explorer中使用Clang 6.0.0的结果是一条jmp没有设置参数寄存器的指令,即使-Wall -Werror添加到编译器选项中也是如此.相反,如果(void)删除该行,则会产生编译器错误.
| 归档时间: |
|
| 查看次数: |
232 次 |
| 最近记录: |