内建函数是编译器内部实现的函数。这些函数跟关键字一样,可以直接使用,无须像标准库函数那样,要 #include
对应的头文件才能使用。
内建函数的函数命名,通常以 __builtin
开头。这些函数主要在编译器内部使用,主要是为编译器服务的。内建函数的主要用途如下:
因为内建函数是编译器内部定义,主要由编译器相关的工具和程序调用,所以这些函数并没有文档说明,而且变动而频繁。对于程序开发者来说,不建议使用这些函数。
有些内建函数,对于我们了解程序运行的底层信息、编译优化很有帮助,而且在 Linux 内核中也经常使用这些函数,所以还是很有必要去了解 Linux 内核中常用的一些内建函数。
__builtin_return_address(LEVEL)
这个函数用来返回当前函数或调用者的返回地址。函数的参数 LEVEl 表示函数调用链中的不同层次的函数,各个值代表的意义如下。
0:返回当前函数的返回地址;
1:返回当前函数调用者的返回地址;
2:返回当前函数调用者的调用者的返回地址。
在函数调用过程中,还有一个 “栈帧” 的概念。函数每调用一次,都会将当前函数的现场(返回地址、寄存器等)保存在栈中,每一层函数调用都会将各自的现场信息都保存在各自的栈中。这个栈也就是当前函数的栈帧,每一个栈帧有起始地址和结束地址,表示当前函数的堆栈信息。
__builtin_return_address(LEVEL)
多层函数调用就会有多个栈帧,每个栈帧里会保存上一层栈帧的起始地址,这样各个栈帧就形成了一个调用链。
很多调试器、GDB、包括我们的这个内建函数,其实都是通过回溯函数栈帧调用链来获取函数底层的各种信息的。
在 ARM 系统中,使用 FP 和 SP 这两个寄存器,分别指向当前函数栈帧的起始地址和结束地址。当函数继续调用或者返回,这两个寄存器的值也会发生变化,总是指向当前函数栈帧的起始地址和结束地址。
+----+ <-- high address
| |
+----+ <- fp // 栈帧起始地址
| |
+----+
| |
+----+
| |
+----+ <- sp // 栈顶
| |
+----+
| |
+----+ <-- low address
我们可以通过内建函数 __builtinframeaddress(LEVEL)
,查看函数的栈帧地址。
0: 查看当前函数的栈帧地址;
1: 查看当前函数调用者的栈帧地址。
…
内建函数 __builtin_expect
也常常用来编译优化。这个函数有两个参数,返回值就是其中一个参数,仍是 exp。这个函数的意义主要就是告诉编译器:参数 exp
的值为 c
的可能性很大。然后编译器可能就会根据这个提示信息,做一些分支预测上的代码优化。
参数 c 跟这个函数的返回值无关,无论 c 为何值,函数的返回值都是 exp
Linux 内核中,使用 __builtin_expect
内建函数,定义了两个宏:
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
这两个宏的主要作用,就是告诉编译器:某一个分支发生的概率很高,或者说很低,基本不可能发生。编译器就根据这个提示信息,就会去做一些分值预测的编译优化。在这两个宏定义有一个细节,就是对宏的参数 x
做两次取非操作,这是为了将参数 x
转换为布尔类型,然后与 1
和 0
作比较,告诉编译器 x 为真或为假的可能性很高。
在 GNU C 编译器内部,实现了一些和 C 标准库函数类似的内建函数。这些函数跟 C 标准库函数功能相似,函数名也相同,只是在前面加了一个前缀 __builtin_
。
在项目开发中会遇到下面编译问题:
warning: conflicting types for built-in function
可以看出来其原因是定义的函数和 所使用的 C 库中的的内建函数重名了,导致编译的时候报错了。这个时候一般通过修改函数名来解决这个问题,其实 GCC 编译器已经给我们提供了解决方法,也即在编译参数选项中增加 -fno-builtin
选项。
rt-thread/rt-thread/bsp/taihu/rtconfig.py
AFLAGS = '-D__ASSEMBLY__ -fno-exceptions -fno-builtin -mregnames -c -Wall ...'
在 PC 中如果编译单个文件可以使用 gcc a.c -o a.out -fno-builtin
如果遇到有的函数想用内建函数,有的不想用内建函数,那该怎么办了? 这时可以使用 GCC 提供的另外一个编译选项:-fno-builtin-function
, 该编译参数仅仅是不把 function
这个函数当作内建(built-in)函数,function这个函数不能够以 "__builtin
_ " 开头。如果一个函数不是以内建(built-in)函数名命名的,那么这个选项(-fno-builtin-function)是无效的。
如果编译选项里使用了 “-fno-builtin
” ,但是你又想有选择性的使用内建(built-in)函数,这个时候可通过定义宏(macros)来解决,例如:
#define abs(s) __builtin_abs((n))
#define strcpy(d, s) __builtin_strcpy((d), (s))
推荐阅读:
https://www.cnblogs.com/zhangzhiwei122/p/15759530.html
http://1024s.top/mbstudy/mbBlog/blogDetail?blogId=7269