【ARM 嵌入式 C 入门及渐进 6 -- Linux 内建函数 __builtin_】

文章目录

    • 1.1 内建函数
      • 1.1.1 内建函数 __builtin_return_address
      • 1.1.2 内建函数 __builtin_frame_address
      • 1.1.3 内建函数 __builtin_expect(exp, c)
      • 1.1.4 C 标准库的内建函数
    • 1.2 内建函数编译选项

1.1 内建函数

内建函数是编译器内部实现的函数。这些函数跟关键字一样,可以直接使用,无须像标准库函数那样,要 #include 对应的头文件才能使用。

内建函数的函数命名,通常以 __builtin 开头。这些函数主要在编译器内部使用,主要是为编译器服务的。内建函数的主要用途如下:

  • 用来处理变长参数列表;
  • 用来处理程序运行异常;
  • 程序的编译优化、性能优化;
  • 查看函数运行中的底层信息、堆栈信息等;
  • C 标准库函数的内建版本。

因为内建函数是编译器内部定义,主要由编译器相关的工具和程序调用,所以这些函数并没有文档说明,而且变动而频繁。对于程序开发者来说,不建议使用这些函数。

1.1.1 内建函数 __builtin_return_address

有些内建函数,对于我们了解程序运行的底层信息、编译优化很有帮助,而且在 Linux 内核中也经常使用这些函数,所以还是很有必要去了解 Linux 内核中常用的一些内建函数。

__builtin_return_address(LEVEL)

这个函数用来返回当前函数或调用者的返回地址。函数的参数 LEVEl 表示函数调用链中的不同层次的函数,各个值代表的意义如下。

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

1.1.2 内建函数 __builtin_frame_address

在函数调用过程中,还有一个 “栈帧” 的概念。函数每调用一次,都会将当前函数的现场(返回地址、寄存器等)保存在栈中,每一层函数调用都会将各自的现场信息都保存在各自的栈中。这个栈也就是当前函数的栈帧,每一个栈帧有起始地址和结束地址,表示当前函数的堆栈信息。

__builtin_return_address(LEVEL)

多层函数调用就会有多个栈帧,每个栈帧里会保存上一层栈帧的起始地址,这样各个栈帧就形成了一个调用链。
很多调试器、GDB、包括我们的这个内建函数,其实都是通过回溯函数栈帧调用链来获取函数底层的各种信息的。

在 ARM 系统中,使用 FPSP 这两个寄存器,分别指向当前函数栈帧的起始地址和结束地址。当函数继续调用或者返回,这两个寄存器的值也会发生变化,总是指向当前函数栈帧的起始地址和结束地址。

+----+ <-- high address
|    | 
+----+ <- fp  // 栈帧起始地址
|    |
+----+
|    |
+----+
|    |
+----+ <- sp  // 栈顶
|    |
+----+
|    | 
+----+ <-- low address

我们可以通过内建函数 __builtinframeaddress(LEVEL),查看函数的栈帧地址。

0: 查看当前函数的栈帧地址;
1: 查看当前函数调用者的栈帧地址。

1.1.3 内建函数 __builtin_expect(exp, c)

内建函数 __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 转换为布尔类型,然后与 10 作比较,告诉编译器 x 为真或为假的可能性很高。

1.1.4 C 标准库的内建函数

在 GNU C 编译器内部,实现了一些和 C 标准库函数类似的内建函数。这些函数跟 C 标准库函数功能相似,函数名也相同,只是在前面加了一个前缀 __builtin_

1.2 内建函数编译选项

在项目开发中会遇到下面编译问题:

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

你可能感兴趣的:(嵌入式,C,常用算法及函数,c语言,内建函数,builtin_reuturn,__builtin)