Tom*_*Tom 24 c gcc gcc-warning noreturn
我正在使用C99下GCC.
我有一个static inline在头文件中声明的函数,我无法修改.
该函数永不返回但未标记__attribute__((noreturn)).
如何以告诉编译器不会返回的方式调用该函数?
我从我自己的noreturn函数调用它,部分想要抑制"noreturn函数返回"警告,但也想帮助优化器等.
我已尝试在属性中包含声明,但会收到有关重复声明的警告.
我已经尝试创建一个函数指针并将该属性应用于该函数,但它表示函数属性不能应用于指向函数.
Ant*_*ine 37
从您定义的函数和调用外部函数的函数中,添加一个__builtin_unreachable内置于至少GCC和Clang编译器的调用并进行标记noreturn.实际上,这个函数什么都不做,不应该调用.它只在这里,以便编译器可以推断程序执行将在此时停止.
static inline external_function() // lacks the noreturn attribute
{ /* does not return */ }
void your_function() __attribute__((noreturn)) {
external_function(); // the compiler thinks execution may continue ...
__builtin_unreachable(); // ... and now it knows it won't go beyond here
}
Run Code Online (Sandbox Code Playgroud)
编辑:只是为了澄清评论中提出的几点,并且通常给出一些背景:
__attribute__((noreturn))是一个注释(如const),这是程序员通知编译器他绝对确定函数不会返回的一种方式.遵循信任但验证原则,编译器试图证明函数确实没有返回.如果它证明函数可能返回则可能发出错误,如果无法证明函数是否返回则发出警告.__builtin_unreachable有未定义的行为,因为它不打算被调用.它只是为了帮助编译器的静态分析.实际上,编译器知道此函数不会返回,因此任何后续代码都可以证明无法访问(除了通过跳转).一旦编译器(通过自身或程序员的帮助)建立了某些代码无法访问,它可以使用此信息进行如下优化:
__builtin_unreachable()无法访问.noreturn.这就是发生的事情your_function.pure函数)可能会被删除.插图: - external_function无法删除呼叫,因为它可能有副作用.事实上,它可能至少有终止过程的副作用!- your_function可以拆除返回锅炉板
这是另一个示例,显示了如何删除无法访问点之前的代码
int compute(int) __attribute((pure)) { return /* expensive compute */ }
if(condition) {
int x = compute(input); // (1) no side effect => keep if x is used
// (8) x is not used => remove
printf("hello "); // (2) reachable + side effect => keep
your_function(); // (3) reachable + side effect => keep
// (4) unreachable beyond this point
printf("word!\n"); // (5) unreachable => remove
printf("%d\n", x); // (6) unreachable => remove
// (7) mark 'x' as unused
} else {
// follows unreachable code, but can jump here
// from reachable code, so this is reachable
do_stuff(); // keep
}
Run Code Online (Sandbox Code Playgroud)
几种解决方案
__attribute__您应该尝试通过添加__attribute__((noreturn))它来修改其标题中的该函数.
您可以使用new属性重新声明某些函数,因为这个愚蠢的测试演示了(添加属性fopen):
#include <stdio.h>
extern FILE *fopen (const char *__restrict __filename,
const char *__restrict __modes)
__attribute__ ((warning ("fopen is used")));
void
show_map_without_care (void)
{
FILE *f = fopen ("/proc/self/maps", "r");
do
{
char lin[64];
fgets (lin, sizeof (lin), f);
fputs (lin, stdout);
}
while (!feof (f));
fclose (f);
}
Run Code Online (Sandbox Code Playgroud)
最后,您可以定义一个宏
#define func(A) {func(A); __builtin_unreachable();}
Run Code Online (Sandbox Code Playgroud)
(这使用了一个事实,即在宏内部,宏名称不是宏扩展的).
如果您从未返回func被声明为返回例如int您将使用的语句表达类似
#define func(A) ({func(A); __builtin_unreachable(); (int)0; })
Run Code Online (Sandbox Code Playgroud)
像上面这样的基于宏的解决方案并不总是有效,例如,如果func作为函数指针传递,或者仅仅是某些人员代码(func)(1)合法但丑陋.
noreturn 属性重新声明静态内联以下示例:
// file ex.c
// declare exit without any standard header
void exit (int);
// define myexit as a static inline
static inline void
myexit (int c)
{
exit (c);
}
// redeclare it as notreturn
static inline void myexit (int c) __attribute__ ((noreturn));
int
foo (int *p)
{
if (!p)
myexit (1);
if (p)
return *p + 2;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当使用GCC 4.9(来自Debian/Sid/x86-64)编译时,as gcc -S -fverbose-asm -O2 ex.c)给出一个包含预期优化的汇编文件:
.type foo, @function
foo:
.LFB1:
.cfi_startproc
testq %rdi, %rdi # p
je .L5 #,
movl (%rdi), %eax # *p_2(D), *p_2(D)
addl $2, %eax #, D.1768
ret
.L5:
pushq %rax #
.cfi_def_cfa_offset 16
movb $1, %dil #,
call exit #
.cfi_endproc
.LFE1:
.size foo, .-foo
Run Code Online (Sandbox Code Playgroud)
您可以使用#pragma GCC诊断来有选择地禁用警告.
最后,您可以gcc使用MELT插件自定义最近的并编写简单的扩展名(使用MELT域特定语言),以便noreturn在包含所需功能时添加属性.它可能是十几个MELT行,使用register_finish_decl_first和匹配函数名称.
由于我是MELT(自由软件GPLv3 +)的主要作者,如果您提出要求,我甚至可以为您编写代码,例如此处或最好是gcc-melt@googlegroups.com; 给出你永不归来的功能的具体名称.
可能MELT代码看起来像:
;;file your_melt_mode.melt
(module_is_gpl_compatible "GPLv3+")
(defun my_finish_decl (decl)
(let ( (tdecl (unbox :tree decl))
)
(match tdecl
(?(tree_function_decl_named
?(tree_identifier ?(cstring_same "your_function_name")))
;;; code to add the noreturn attribute
;;; ....
))))
(register_finish_decl_first my_finish_decl)
Run Code Online (Sandbox Code Playgroud)
真正的MELT代码稍微复杂一些.你想在your_adding_attr_mode那里定义.向我询问更多.
一旦你编码您MELT扩展your_melt_mode.melt 您的需求(和编译融化拓成your_melt_mode.quicklybuilt.so作为记录在熔融教程),你会用编译代码
gcc -fplugin=melt \
-fplugin-arg-melt-extra=your_melt_mode.quicklybuilt \
-fplugin-arg-melt-mode=your_adding_attr_mode \
-O2 -I/your/include -c yourfile.c
Run Code Online (Sandbox Code Playgroud)
换句话说,你只需要添加一些-fplugin-*标志到CFLAGS你Makefile!
顺便说一句,我只是在MELT监视器中进行编码(在github上:https://github.com/bstarynk/melt-monitor ...,提交meltmom-process.melt一些非常相似的文件.
使用MELT扩展,您将不会收到任何其他警告,因为MELT扩展将动态更改声明函数的内部GCC AST(GCC 树)!
使用MELT定制GCC可能是最具防弹性的解决方案,因为它正在修改GCC内部AST.当然,它可能是最昂贵的解决方案(并且它是GCC特定的,并且可能需要 - 当GCC正在发展时变小,例如当使用下一版本的GCC时),但是正如我试图表明它很容易你的情况.