我不知道有什么方法可以告诉编译器它可以进行不同的调用,但在这种情况下,我们可以用不同的方式实现相同的目标。
只对编译时常数使用特殊情况的常用方法是
__builtin_constant_p()
在内联+常量折叠之后,测试变量的值在编译时是否已知。(老Clang用来评估
__内置常量p()
太早了,在内联之前,所以函数参数永远不会是常量,这使得它除了在宏中之外有些无用。但这已经被修复,使其发挥作用
like GCC intended
.)
这个
_p
GCC内置程序的命名
comes from Lisp
。这是一个“谓词”函数,它询问一个是/否问题并返回一个布尔值。
void my_putchar(char c) {
if (__builtin_constant_p(c)) {
// only reached if c is a compile-time constant after inlining
if (c == '\n') { call_line_feed_out(); return; }
else if (c == '\a') { call_bell_out(); return; }
}
// else runtime variable or a constant that wasn't one of those special cases
call_cout(c);
}
的非内联定义
my_putchar
只是对
call_cout
。(如果我们能以某种方式将该符号作为的别名,那就更好了
调用_输出
因此执行不必通过这一个指令函数。)
或者这只是一个常规的电话
调用_输出
当与非常量内联时,或与除之外的字符内联时
'\n'
或
'\a'
.
void use_putchar_newline(void){
my_putchar('\n');
}
编译为
b call_line_feed_out
-尾随。
void use_putchar_test(char *str){
my_putchar(str[0]);
my_putchar(str[1]);
my_putchar(str[2]);
const char *strconst = "ab\n";
my_putchar(strconst[0]);
my_putchar(strconst[1]);
my_putchar(strconst[2]);
}
为AArch64编译
clang on Godbolt
-O3 -fomit-frame-pointer
。(我想
-fomit-frame-pointer
默认情况下将在打开
-O3
对于Linux,但显然不是。)
use_putchar_test:
stp x30, x19, [sp, #-16]! // 16-byte Folded Spill // save return address and a call-preserved reg
mov x19, x0 // copy str to a call-preserved reg
ldrb w0, [x0] // my_putchar(str[0]);
bl call_cout
ldrb w0, [x19, #1]
bl call_cout
ldrb w0, [x19, #2]
bl call_cout // my_putchar(str[2]);
mov w0, #97
bl call_cout // my_putchar(strconst[0])
mov w0, #98
bl call_cout
ldp x30, x19, [sp], #16 // 16-byte Folded Reload
b call_line_feed_out // my_putchar(strconst[2]) tailcall
因此,对于未知的运行时变量输入,我们没有运行时分支,对于字符串文字中的常量换行符,我们仍然可以调度到特殊情况函数。
GCC对它的编译大致相同。