当前位置:首页 » 《随便一记》 » 正文

GCC特性——内建函数

18 人参与  2022年11月29日 13:45  分类 : 《随便一记》  评论

点击全文阅读


1. __builtin_return_address()

函数原型: 

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 值:

0: 返回当前函数的返回地址;1: 返回当前函数调用者的返回地址;2: 返回当前函数调用者的调用者的返回地址;...

例如:

mm/vmalloc.cstruct 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 <stdio.h>#include <stdlib.h>#include <signal.h>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) rStarting program: /home/justinwei/test/c/test64/testenter maintest: 0x5555554006f9a: 0x5555554006d3b: 0x55555540068aa(0): 0x555555400702b(0): 0x555555400707b(1): 0x555555400767exit main

a(0) 通过 __builtin_return_addrest(0) 打印的值为 0x555555400702

(gdb) l *0x5555554007020x555555400702 is in test() (test.cpp:20).15      }1617      void test()18      {19          a();20          b();21      }222324      int main()

C 语言函数在调用过程中,会将当前函数的返回地址、寄存器等现场信息保存在堆栈中,然后才会跳到被调用函数中去执行。当被调用函数执行结束后,根据保存在堆栈中的返回地址,就可以直接返回到原来的函数中继续执行。

在此程序中,test() 中准备调用 a() 和 b(),在跳转到a() 执行之前,会将程序正在运行的当前语句的下一条语句的地址保存到堆栈中,然后才去执行 a()。在a() 执行完毕后,将保存在堆栈中的返回地址赋值给 PC 指针,就可以返回到 test() 继续执行。

2. __builtin_freme_address()

函数原型:

     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.cvoid 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);}

3. __builtin_constant_p()

函数原型:

     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)\

4. __builtin_expect(exp,c)

__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 = 3a = 3a = 3

5. likely() & unlikely()

# define likely(x)__builtin_expect(!!(x), 1)# define unlikely(x)__builtin_expect(!!(x), 0)

likely() 和 unlikely() 是使用 __builtin_expect() 定义的两个宏,这两个宏的主要作用,就是告诉编译器:某一个分支发生的概率很高,或者说很低,基本不可能发生。编译器就根据这个提示信息,就会去做一些分值预测的编译优化。在这两个宏定义有一个细节,就是对宏的参数 x 做两次取非操作,这是为了将参数 x 转换为布尔类型,然后与 1 和 0 作比较,告诉编译器 x 为真或为假的可能性很高。

 

 

 

参考:

https://man.netbsd.org/NetBSD-6.1/__builtin_constant_p.3 


点击全文阅读


本文链接:http://zhangshiyu.com/post/49262.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1