GCC特性——内建函数
创始人
2024-02-23 22:55:36
0

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 
#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() 继续执行。

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

相关内容

热门资讯

原创 仿... 【版权申明:本文为@影吹斯汀 独家原创稿,未经许可不得以任何形式抄袭or转载,违者必究!】 由詹姆斯...
女子称在亚朵酒店内用牙签,入口... 原标题:女子称在亚朵酒店内用牙签,入口后猛然发现可能是别人用过的,店方回应 近日,有网友发帖称,自己...
捡到宝了!泰国上将:中国没要求... 当地时间2025年12月15日,泰国国防部长对于,泰国特种兵在战场缴获大批,柬埔寨军人放弃的,中国制...
涉嫌严重违纪违法,李舜被查 据省纪委监委驻省教育厅纪检监察组、玉溪市监委消息:云南省教育厅教材和语言文字管理处原处长、一级调研员...
原创 鞠... 2025年12月19日,最近身陷流言蜚语的鞠婧祎被拍到低调现身街头,这是她与经纪公司丝芭传媒爆发合约...
广州越秀南路有两人受伤,警方通... 12月19日晚,广州市公安局越秀分局发布警情通报:2025年12月19日17时37分,广州110接群...
原创 金... 最近,大家都注意到特朗普单方面宣布允许英伟达对华出售H200芯片。作为交换,美国政府将对H200的所...
明年起,成都全域禁止使用国Ⅰ及... 12月19日,记者从成都市生态环境局获悉,《成都市人民政府关于调整高排放非道路移动机械禁止使用区域的...
闪电进球!图拉姆1分12秒凌空... 在意大利超级杯半决赛的激烈对抗中,国米与博洛尼亚的较量可谓是一场战术与激情的巅峰对决。开场仅1分12...
美联储“三把手”淡化降息预期:... 智通财经APP获悉,美联储“三把手”、纽约联储主席威廉姆斯表示,目前没有迫切需要进一步调整利率政策,...