函数原型:
void * __builtin_return_address(unsigned int level);
描述:
The __builtin_return_address() is a GNU extension for obtaining the
return address of the current function or one of the callers of the cur-
rent function.The parameter level specifies the number of frames that should be scanned
up in the call stack. A value 0 returns the address of the current func-
tion, a value 1 requests the address of the caller of the current func-
tion, a value 2 asks for the address of the caller's caller, and so
forth. If the top of the call stack has been reached, the function will
return 0. Note also that on some architectures it is only possible to
determine the address of the current function. In such cases a value 0
is returned. Thus, it is usually safe to only use the value 0 for level.
该函数是GNU 扩展,用以返回当前函数或者当前函数调用者的返回地址。
参数 level 表示函数调用栈中不同层次的 frame 值:
例如:
- mm/vmalloc.c
-
- struct vm_struct *get_vm_area(unsigned long size, unsigned long flags)
- {
- return __get_vm_area_node(size, 1, flags, VMALLOC_START, VMALLOC_END,
- NUMA_NO_NODE, GFP_KERNEL,
- __builtin_return_address(0));
- }
测试用例:
- #include
- #include
- #include
-
-
- void b()
- {
- printf("%s(0): %p\n", __func__, __builtin_return_address(0));
- printf("%s(1): %p\n", __func__, __builtin_return_address(1));
- }
-
- void a()
- {
- printf("%s(0): %p\n", __func__, __builtin_return_address(0));
- }
-
- void test()
- {
- a();
- b();
- }
-
-
- int main()
- {
- printf("enter main\n");
-
- printf("test: %p\n", test);
- printf("a: %p\n", a);
- printf("b: %p\n", b);
-
- test();
-
- printf("exit main\n");
-
- return 0;
- }
我们来看下gdb 运行结果:
- (gdb) r
- Starting program: /home/justinwei/test/c/test64/test
- enter main
- test: 0x5555554006f9
- a: 0x5555554006d3
- b: 0x55555540068a
- a(0): 0x555555400702
- b(0): 0x555555400707
- b(1): 0x555555400767
- exit main
a(0) 通过 __builtin_return_addrest(0) 打印的值为 0x555555400702:
- (gdb) l *0x555555400702
- 0x555555400702 is in test() (test.cpp:20).
- 15 }
- 16
- 17 void test()
- 18 {
- 19 a();
- 20 b();
- 21 }
- 22
- 23
- 24 int main()
C 语言函数在调用过程中,会将当前函数的返回地址、寄存器等现场信息保存在堆栈中,然后才会跳到被调用函数中去执行。当被调用函数执行结束后,根据保存在堆栈中的返回地址,就可以直接返回到原来的函数中继续执行。
在此程序中,test() 中准备调用 a() 和 b(),在跳转到a() 执行之前,会将程序正在运行的当前语句的下一条语句的地址保存到堆栈中,然后才去执行 a()。在a() 执行完毕后,将保存在堆栈中的返回地址赋值给 PC 指针,就可以返回到 test() 继续执行。
函数原型:
void *__builtin_frame_address(unsigned int level);
描述:
The __builtin_frame_address() behaves similarly, but returns the address
of the function frame rather than the return address of the function.
与__builtin_return_address() 类似,返回的是函数栈帧的地址,而不是函数的返回地址。
在函数调用过程中,还有一个“栈帧”的概念。函数每调用一次,都会将当前函数的现场(返回地址、寄存器等)保存在栈中,每一层函数调用都会将各自的现场信息都保存在各自的栈中。这个栈也就是当前函数的栈帧,每一个栈帧有起始地址和结束地址,表示当前函数的堆栈信息。多层函数调用就会有多个栈帧,每个栈帧里会保存上一层栈帧的起始地址,这样各个栈帧就形成了一个调用链。很多调试器、GDB、包括我们的这个内建函数,其实都是通过回溯函数栈帧调用链来获取函数底层的各种信息的。比如,返回地址 i、调用关系等。在 ARM 系统中,使用 FP 和 SP 这两个寄存器,分别指向当前函数栈帧的起始地址和结束地址。当函数继续调用或者返回,这两个寄存器的值也会发生变化,总是指向当前函数栈帧的起始地址和结束地址。
例如:
- mm/ksan/common.c
-
- void kasan_unpoison_stack_above_sp_to(const void *watermark)
- {
- const void *sp = __builtin_frame_address(0);
- size_t size = watermark - sp;
-
- if (WARN_ON(sp > watermark))
- return;
- kasan_unpoison_shadow(sp, size);
- }
函数原型:
int __builtin_constant_p(value);
描述:
The __builtin_constant_p() is a GNU extension for determining whether a
value is known to be constant at compile time. The function is closely
related to the concept of ``constant folding'' used by modern optimizing
compilers.If the value is known to be a compile-time constant, a value 1 is
returned. If __builtin_constant_p() returns 0, the value is not a com-
pile-time constant in the sense that gcc(1) was unable to determine
whether the value is constant or not.
在编译时,是否为常量,是常量则返回1,否则返回0.
该函数长用于宏定义中,用于编译优化。一个宏定义,根据宏的参数是常量还是变量,可能实现的方式不一样。
例如:
- include/linux/wait.h
-
- #define ___wait_is_interruptible(state) \
- (!__builtin_constant_p(state) || \
- state == TASK_INTERRUPTIBLE || state == TASK_KILLABLE) \
__builtin_expect() 也常常用来编译优化。这个函数有两个参数,返回值就是其中一个参数,仍是 exp。这个函数的意义主要就是告诉编译器:参数 exp 的值为 c 的可能性很大。然后编译器可能就会根据这个提示信息,做一些分支预测上的代码优化。
参数 c,与函数的返回值无关,无论 c 为何值,函数的返回值都是 exp
例如:
- int main(void)
- {
- int a;
- a = __builtin_expect(3,1);
- printf("a = %d\n",a);
- a = __builtin_expect(3,10);
- printf("a = %d\n",a);
- a = __builtin_expect(3,100);
- printf("a = %d\n",a);
- return 0;
- }
程序运行结果:
- a = 3
- a = 3
- a = 3
- # define likely(x) __builtin_expect(!!(x), 1)
- # define unlikely(x) __builtin_expect(!!(x), 0)
likely() 和 unlikely() 是使用 __builtin_expect() 定义的两个宏,这两个宏的主要作用,就是告诉编译器:某一个分支发生的概率很高,或者说很低,基本不可能发生。编译器就根据这个提示信息,就会去做一些分值预测的编译优化。在这两个宏定义有一个细节,就是对宏的参数 x 做两次取非操作,这是为了将参数 x 转换为布尔类型,然后与 1 和 0 作比较,告诉编译器 x 为真或为假的可能性很高。
__builtin_clz() 也是 GCC 和 Clang 编译器提供的一个内置函数,用于计算一个整数的二进制表示中,从最高位开始连续的0的个数。
该函数的使用背景是在一些位运算和计算机视觉等领域中,需要对二进制数据进行处理和分析,而从最高位开始连续的0的个数是一个常见的计算需求。
__builtin_clz() 的内部原理是使用 CPU 的指令集来实现计算。具体来说,当 CPU 支持 CLZ 指令时,__builtin_clz() 会使用CLZ指令来计算从最高位开始连续的0的个数;否则,__builtin_clz() 会使用一些位运算技巧来实现计算。在实现中,__builtin_clz() 会根据不同的 CPU 架构和编译器选项来选择最优的实现方式,从而提高计算效率。
__builtin_clz() 的弊端是可能会导致代码的可移植性问题。由于__builtin_clz() 是 GCC 和 Clang 编译器提供的一个内置函数,因此在使用 __builtin_clz() 时,需要确保代码的可移植性,并且需要在代码中添加条件编译来处理不支持 CLZ 指令的 CPU。另外,由于__builtin_clz() 的实现依赖于CPU 架构和编译器选项,因此在不同的平台和编译器下,__builtin_clz() 的性能可能会有所不同。在使用__builtin_clz() 时,需要进行性能测试,并根据实际情况选择最优的实现方式。
与__builtin_clz() 相似的是 __builtin_ctz() 用于计算一个整数的二进制表示中,从最低位开始连续的 0 的个数.
__builtin_ctz() 的内部原理是使用 CPU 的指令集来实现计算。具体来说,当 CPU 支持 CTZ 指令时,__builtin_ctz() 会使用CTZ指令来计算从最低位开始连续的0的个数;否则,__builtin_ctz会使用一些位运算技巧来实现计算。
__builtin_clzll 与 __builtin_ctzll则是64位对应的实现版本。
参考: